Merge "Make SUPL satellite message constrained" into main
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
index a40adac..82ef3e6 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "10093150"
+ build_id: "11947186"
target: "CtsShim"
source_file: "aosp_arm64/CtsShimPriv.apk"
}
@@ -8,7 +8,7 @@
version: ""
version_group: ""
git_project: "platform/frameworks/base"
- git_branch: "udc-dev"
+ git_branch: "main"
transform: TRANSFORM_NONE
transform_options {
}
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
index 96444ba..7d0e5d7 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "10093150"
+ build_id: "11947186"
target: "CtsShim"
source_file: "aosp_arm64/CtsShim.apk"
}
@@ -8,7 +8,7 @@
version: ""
version_group: ""
git_project: "platform/frameworks/base"
- git_branch: "udc-dev"
+ git_branch: "main"
transform: TRANSFORM_NONE
transform_options {
}
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
index 4d6f8ed..be32060 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "10093150"
+ build_id: "11947186"
target: "CtsShim"
source_file: "aosp_x86_64/CtsShimPriv.apk"
}
@@ -8,7 +8,7 @@
version: ""
version_group: ""
git_project: "platform/frameworks/base"
- git_branch: "udc-dev"
+ git_branch: "main"
transform: TRANSFORM_NONE
transform_options {
}
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
index bfd6788..1a6448a 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "10093150"
+ build_id: "11947186"
target: "CtsShim"
source_file: "aosp_x86_64/CtsShim.apk"
}
@@ -8,7 +8,7 @@
version: ""
version_group: ""
git_project: "platform/frameworks/base"
- git_branch: "udc-dev"
+ git_branch: "main"
transform: TRANSFORM_NONE
transform_options {
}
diff --git a/Android.bp b/Android.bp
index af312bf..fb1fa3b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -115,7 +115,7 @@
":android.security.legacykeystore-java-source",
":android.security.maintenance-java-source",
":android.security.metrics-java-source",
- ":android.system.keystore2-V3-java-source",
+ ":android.system.keystore2-V4-java-source",
":android.hardware.cas-V1-java-source",
":credstore_aidl",
":dumpstate_aidl",
@@ -404,6 +404,7 @@
"android.hardware.common.fmq-V1-java",
"bouncycastle-repackaged-unbundled",
"com.android.sysprop.foldlockbehavior",
+ "com.android.sysprop.view",
"framework-internal-utils",
// If MimeMap ever becomes its own APEX, then this dependency would need to be removed
// in favor of an API stubs dependency in java_library "framework" below.
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index e857175..fea2b7b 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -30,7 +30,7 @@
ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py --no-verify-format -f ${PREUPLOAD_FILES}
# This flag check hook runs only for "packages/SystemUI" subdirectory. If you want to include this check for other subdirectories, please modify flag_check.py.
-flag_hook = ${REPO_ROOT}/frameworks/base/packages/SystemUI/flag_check.py --msg=${PREUPLOAD_COMMIT_MESSAGE} --files=${PREUPLOAD_FILES} --project=${REPO_PATH}
+flag_hook = ${REPO_ROOT}/frameworks/base/packages/SystemUI/flag_check.py --msg=${PREUPLOAD_COMMIT_MESSAGE} --files=${PREUPLOAD_FILES} --project=${REPO_PROJECT}
[Tool Paths]
-ktfmt = ${REPO_ROOT}/prebuilts/build-tools/common/framework/ktfmt.jar
+ktfmt = ${REPO_ROOT}/external/ktfmt/ktfmt.sh
diff --git a/apct-tests/perftests/core/src/android/permission/AppOpsPerfTest.kt b/apct-tests/perftests/core/src/android/permission/AppOpsPerfTest.kt
index 2af878e..daf991c 100644
--- a/apct-tests/perftests/core/src/android/permission/AppOpsPerfTest.kt
+++ b/apct-tests/perftests/core/src/android/permission/AppOpsPerfTest.kt
@@ -17,8 +17,7 @@
import android.app.AppOpsManager
import android.content.Context
-import androidx.benchmark.BenchmarkState
-import androidx.benchmark.junit4.BenchmarkRule
+import android.perftests.utils.PerfStatusReporter
import androidx.test.core.app.ApplicationProvider
import androidx.test.filters.LargeTest
import org.junit.Before
@@ -34,7 +33,7 @@
* these APIs should be monitored closely for performance.
*/
class AppOpsPerfTest {
- @get:Rule val mBenchmarkRule: BenchmarkRule = BenchmarkRule()
+ @get:Rule val perfStatusReporter = PerfStatusReporter()
private lateinit var appOpsManager: AppOpsManager
private lateinit var opPackageName: String
private var opPackageUid: Int = 0
@@ -49,7 +48,7 @@
@Test
fun testNoteOp() {
- val state: BenchmarkState = mBenchmarkRule.getState()
+ val state = perfStatusReporter.benchmarkState
while (state.keepRunning()) {
appOpsManager.noteOp(
AppOpsManager.OPSTR_FINE_LOCATION,
@@ -63,7 +62,7 @@
@Test
fun testUnsafeCheckOp() {
- val state: BenchmarkState = mBenchmarkRule.getState()
+ val state = perfStatusReporter.benchmarkState
while (state.keepRunning()) {
appOpsManager.unsafeCheckOp(
AppOpsManager.OPSTR_FINE_LOCATION,
diff --git a/cmds/bootanimation/Android.bp b/cmds/bootanimation/Android.bp
index 98767ee..3534624 100644
--- a/cmds/bootanimation/Android.bp
+++ b/cmds/bootanimation/Android.bp
@@ -74,7 +74,4 @@
"libGLESv2",
"libgui",
],
- whole_static_libs: [
- "libc++fs",
- ],
}
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index 6e51f00..58763a7 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -313,7 +313,6 @@
"libziparchive",
],
static_libs: [
- "libc++fs",
"libidmap2_policies",
"libidmap2_protos",
"libidmap2daidl",
diff --git a/cmds/uiautomator/library/Android.bp b/cmds/uiautomator/library/Android.bp
index cffc078..cd1fb9a 100644
--- a/cmds/uiautomator/library/Android.bp
+++ b/cmds/uiautomator/library/Android.bp
@@ -35,7 +35,7 @@
],
installable: false,
flags: [
- "-stubpackages com.android.uiautomator.core:com.android.uiautomator.testrunner",
+ "--stub-packages com.android.uiautomator.core:com.android.uiautomator.testrunner",
],
check_api: {
diff --git a/core/api/current.txt b/core/api/current.txt
index a819b6e..831cf01 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -5605,7 +5605,6 @@
method @Deprecated public void onCancel(android.content.DialogInterface);
method @Deprecated public android.app.Dialog onCreateDialog(android.os.Bundle);
method @Deprecated public void onDismiss(android.content.DialogInterface);
- method public android.view.LayoutInflater onGetLayoutInflater(android.os.Bundle);
method @Deprecated public void setCancelable(boolean);
method @Deprecated public void setShowsDialog(boolean);
method @Deprecated public void setStyle(int, int);
@@ -15975,6 +15974,7 @@
enum_constant public static final android.graphics.ColorSpace.Named LINEAR_EXTENDED_SRGB;
enum_constant public static final android.graphics.ColorSpace.Named LINEAR_SRGB;
enum_constant public static final android.graphics.ColorSpace.Named NTSC_1953;
+ enum_constant @FlaggedApi("com.android.graphics.flags.ok_lab_colorspace") public static final android.graphics.ColorSpace.Named OK_LAB;
enum_constant public static final android.graphics.ColorSpace.Named PRO_PHOTO_RGB;
enum_constant public static final android.graphics.ColorSpace.Named SMPTE_C;
enum_constant public static final android.graphics.ColorSpace.Named SRGB;
@@ -19911,7 +19911,6 @@
}
@FlaggedApi("com.android.internal.camera.flags.concert_mode_api") public final class ExtensionCaptureRequest {
- ctor public ExtensionCaptureRequest();
field @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Boolean> EFV_AUTO_ZOOM;
field @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> EFV_MAX_PADDING_ZOOM_FACTOR;
field @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> EFV_PADDING_ZOOM_FACTOR;
@@ -19924,7 +19923,6 @@
}
@FlaggedApi("com.android.internal.camera.flags.concert_mode_api") public final class ExtensionCaptureResult {
- ctor public ExtensionCaptureResult();
field @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Boolean> EFV_AUTO_ZOOM;
field @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") @NonNull public static final android.hardware.camera2.CaptureResult.Key<int[]> EFV_AUTO_ZOOM_PADDING_REGION;
field @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> EFV_MAX_PADDING_ZOOM_FACTOR;
@@ -34056,6 +34054,7 @@
field public static final String DISALLOW_BLUETOOTH_SHARING = "no_bluetooth_sharing";
field public static final String DISALLOW_CAMERA_TOGGLE = "disallow_camera_toggle";
field public static final String DISALLOW_CELLULAR_2G = "no_cellular_2g";
+ field @FlaggedApi("android.nfc.enable_nfc_user_restriction") public static final String DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO = "no_change_near_field_communication_radio";
field public static final String DISALLOW_CHANGE_WIFI_STATE = "no_change_wifi_state";
field public static final String DISALLOW_CONFIG_BLUETOOTH = "no_config_bluetooth";
field public static final String DISALLOW_CONFIG_BRIGHTNESS = "no_config_brightness";
@@ -47134,6 +47133,8 @@
method public int describeContents();
method public int getAttributeFlags();
method @NonNull public java.util.Set<java.lang.String> getFeatureTags();
+ method @FlaggedApi("com.android.internal.telephony.flags.emergency_registration_state") public boolean getFlagRegistrationTypeEmergency();
+ method @FlaggedApi("com.android.internal.telephony.flags.emergency_registration_state") public boolean getFlagVirtualRegistrationForEmergencyCall();
method @Nullable public android.telephony.ims.SipDetails getSipDetails();
method public int getTransportType();
method public void writeToParcel(@NonNull android.os.Parcel, int);
diff --git a/core/api/lint-baseline.txt b/core/api/lint-baseline.txt
index 1b0da05..e6a7ca5 100644
--- a/core/api/lint-baseline.txt
+++ b/core/api/lint-baseline.txt
@@ -245,14 +245,6 @@
Field 'ACTION_NOTIFY_CARRIER_SETUP_INCOMPLETE' is missing @BroadcastBehavior
-CompileTimeConstant: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_GIMBAL:
- All constants must be defined at compile time: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_GIMBAL
-CompileTimeConstant: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED:
- All constants must be defined at compile time: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED
-CompileTimeConstant: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_OFF:
- All constants must be defined at compile time: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_OFF
-
-
DeprecationMismatch: android.accounts.AccountManager#newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, String[], boolean, String, String, String[], android.os.Bundle):
Method android.accounts.AccountManager.newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, String[], boolean, String, String, String[], android.os.Bundle): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
DeprecationMismatch: android.app.Activity#enterPictureInPictureMode():
@@ -1095,14 +1087,6 @@
Method 'setGeolocationEnabled' documentation mentions permissions without declaring @RequiresPermission
-StaticUtils: ExtensionCaptureRequest:
- Fully-static utility classes must not have constructor
-StaticUtils: android.hardware.camera2.ExtensionCaptureRequest:
- Fully-static utility classes must not have constructor
-StaticUtils: android.hardware.camera2.ExtensionCaptureResult:
- Fully-static utility classes must not have constructor
-
-
Todo: android.hardware.camera2.params.StreamConfigurationMap:
Documentation mentions 'TODO'
Todo: android.provider.ContactsContract.RawContacts#newEntityIterator(android.database.Cursor):
@@ -1462,14 +1446,6 @@
UnflaggedApi: android.graphics.text.PositionedGlyphs#getWeightOverride(int):
New API must be flagged with @FlaggedApi: method android.graphics.text.PositionedGlyphs.getWeightOverride(int)
-UnflaggedApi: android.hardware.camera2.ExtensionCaptureRequest:
- New API must be flagged with @FlaggedApi: class android.hardware.camera2.ExtensionCaptureRequest
-UnflaggedApi: android.hardware.camera2.ExtensionCaptureRequest#ExtensionCaptureRequest():
- New API must be flagged with @FlaggedApi: constructor android.hardware.camera2.ExtensionCaptureRequest()
-UnflaggedApi: android.hardware.camera2.ExtensionCaptureResult:
- New API must be flagged with @FlaggedApi: class android.hardware.camera2.ExtensionCaptureResult
-UnflaggedApi: android.hardware.camera2.ExtensionCaptureResult#ExtensionCaptureResult():
- New API must be flagged with @FlaggedApi: constructor android.hardware.camera2.ExtensionCaptureResult()
UnflaggedApi: android.media.MediaRoute2Info#TYPE_REMOTE_CAR:
New API must be flagged with @FlaggedApi: field android.media.MediaRoute2Info.TYPE_REMOTE_CAR
UnflaggedApi: android.media.MediaRoute2Info#TYPE_REMOTE_COMPUTER:
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 14ae3f5..d03dd16 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -46,6 +46,7 @@
field public static final String REMAP_MODIFIER_KEYS = "android.permission.REMAP_MODIFIER_KEYS";
field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
field public static final String REQUEST_UNIQUE_ID_ATTESTATION = "android.permission.REQUEST_UNIQUE_ID_ATTESTATION";
+ field public static final String RESERVED_FOR_TESTING_SIGNATURE = "android.permission.RESERVED_FOR_TESTING_SIGNATURE";
field public static final String RESET_APP_ERRORS = "android.permission.RESET_APP_ERRORS";
field public static final String REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL = "android.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL";
field public static final String SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS = "android.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS";
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index 6cc71e5..b4a3abc 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -1973,6 +1973,8 @@
UnflaggedApi: android.Manifest.permission#MANAGE_REMOTE_AUTH:
New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_REMOTE_AUTH
+UnflaggedApi: android.Manifest.permission#RESERVED_FOR_TESTING_SIGNATURE:
+ New API must be flagged with @FlaggedApi: field android.Manifest.permission.RESERVED_FOR_TESTING_SIGNATURE
UnflaggedApi: android.Manifest.permission#START_ACTIVITIES_FROM_SDK_SANDBOX:
New API must be flagged with @FlaggedApi: field android.Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX
UnflaggedApi: android.Manifest.permission#USE_REMOTE_AUTH:
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 79e2bd4..807fa48 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -7555,6 +7555,12 @@
* the activity to be restarted). Otherwise, this will be used the next
* time the activity is visible.
*
+ * <aside class="note"><b>Note:</b> Device manufacturers can configure devices to override
+ * (ignore) calls to this method to improve the layout of orientation-restricted apps. See
+ * <a href="{@docRoot}guide/practices/device-compatibility-mode">
+ * Device compatibility mode</a>.
+ * </aside>
+ *
* @param requestedOrientation An orientation constant as used in
* {@link ActivityInfo#screenOrientation ActivityInfo.screenOrientation}.
*/
@@ -9291,11 +9297,11 @@
if (DEBUG_LIFECYCLE) Slog.v(TAG,
"dispatchMultiWindowModeChanged " + this + ": " + isInMultiWindowMode
+ " " + newConfig);
+ mIsInMultiWindowMode = isInMultiWindowMode;
mFragments.dispatchMultiWindowModeChanged(isInMultiWindowMode, newConfig);
if (mWindow != null) {
mWindow.onMultiWindowModeChanged();
}
- mIsInMultiWindowMode = isInMultiWindowMode;
onMultiWindowModeChanged(isInMultiWindowMode, newConfig);
}
@@ -9304,11 +9310,11 @@
if (DEBUG_LIFECYCLE) Slog.v(TAG,
"dispatchPictureInPictureModeChanged " + this + ": " + isInPictureInPictureMode
+ " " + newConfig);
+ mIsInPictureInPictureMode = isInPictureInPictureMode;
mFragments.dispatchPictureInPictureModeChanged(isInPictureInPictureMode, newConfig);
if (mWindow != null) {
mWindow.onPictureInPictureModeChanged(isInPictureInPictureMode);
}
- mIsInPictureInPictureMode = isInPictureInPictureMode;
onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig);
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 9785252..83c3bf6 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -477,6 +477,11 @@
*/
public static final int OOM_ADJ_REASON_COMPONENT_DISABLED = 22;
+ /**
+ * Oom Adj Reason: Follow up update for time sensitive state evaluations.
+ */
+ public static final int OOM_ADJ_REASON_FOLLOW_UP = 23;
+
@IntDef(prefix = {"OOM_ADJ_REASON_"}, value = {
OOM_ADJ_REASON_NONE,
OOM_ADJ_REASON_ACTIVITY,
@@ -501,6 +506,7 @@
OOM_ADJ_REASON_EXECUTING_SERVICE,
OOM_ADJ_REASON_RESTRICTION_CHANGE,
OOM_ADJ_REASON_COMPONENT_DISABLED,
+ OOM_ADJ_REASON_FOLLOW_UP,
})
@Retention(RetentionPolicy.SOURCE)
public @interface OomAdjReason {}
diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java
index f8a8f5d..b21defb 100644
--- a/core/java/android/app/AppOpsManagerInternal.java
+++ b/core/java/android/app/AppOpsManagerInternal.java
@@ -172,11 +172,9 @@
* @param virtualDeviceId the device for which to finish the op
* @param superImpl
*/
- default void finishOperation(IBinder clientId, int code, int uid, String packageName,
+ void finishOperation(IBinder clientId, int code, int uid, String packageName,
String attributionTag, int virtualDeviceId, @NonNull HexConsumer<IBinder, Integer,
- Integer, String, String, Integer> superImpl) {
- superImpl.accept(clientId, code, uid, packageName, attributionTag, virtualDeviceId);
- }
+ Integer, String, String, Integer> superImpl);
/**
* Allows overriding finish proxy op.
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index cf06416..5bc0ddc 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -42,6 +42,7 @@
import android.service.notification.ZenPolicy;
import android.app.AutomaticZenRule;
import android.service.notification.ZenModeConfig;
+import android.service.notification.ZenDeviceEffects;
/** {@hide} */
interface INotificationManager
@@ -227,6 +228,7 @@
int getRuleInstanceCount(in ComponentName owner);
int getAutomaticZenRuleState(String id);
void setAutomaticZenRuleState(String id, in Condition condition);
+ void setManualZenRuleDeviceEffects(in ZenDeviceEffects effects);
byte[] getBackupPayload(int user);
void applyRestore(in byte[] payload, int user);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index fc3bb02..a1fa404 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -2239,9 +2239,6 @@
private void visitUris(@NonNull Consumer<Uri> visitor) {
visitIconUri(visitor, getIcon());
- if (actionIntent != null) {
- actionIntent.visitUris(visitor);
- }
}
@Override
@@ -2957,21 +2954,6 @@
}
}
- // allPendingIntents should contain all associated intents after parcelling, but it may also
- // contain intents added by the app to extras for their own purposes. We only care about
- // checking the intents known and used by system_server, to avoid the confused deputy issue.
- List<PendingIntent> pendingIntents = Arrays.asList(contentIntent, deleteIntent,
- fullScreenIntent);
- for (PendingIntent intent : pendingIntents) {
- if (intent != null) {
- intent.visitUris(visitor);
- }
- }
-
- if (mBubbleMetadata != null) {
- mBubbleMetadata.visitUris(visitor);
- }
-
if (extras != null) {
visitIconUri(visitor, extras.getParcelable(EXTRA_LARGE_ICON_BIG, Icon.class));
visitIconUri(visitor, extras.getParcelable(EXTRA_PICTURE_ICON, Icon.class));
@@ -3043,28 +3025,15 @@
callPerson.visitUris(visitor);
}
visitIconUri(visitor, extras.getParcelable(EXTRA_VERIFICATION_ICON, Icon.class));
+ }
- // Extras for MediaStyle.
- PendingIntent deviceIntent = extras.getParcelable(EXTRA_MEDIA_REMOTE_INTENT,
- PendingIntent.class);
- if (deviceIntent != null) {
- deviceIntent.visitUris(visitor);
- }
+ if (mBubbleMetadata != null) {
+ visitIconUri(visitor, mBubbleMetadata.getIcon());
+ }
- if (extras.containsKey(WearableExtender.EXTRA_WEARABLE_EXTENSIONS)) {
- WearableExtender extender = new WearableExtender(this);
- extender.visitUris(visitor);
- }
-
- if (extras.containsKey(TvExtender.EXTRA_TV_EXTENDER)) {
- TvExtender extender = new TvExtender(this);
- extender.visitUris(visitor);
- }
-
- if (extras.containsKey(CarExtender.EXTRA_CAR_EXTENDER)) {
- CarExtender extender = new CarExtender(this);
- extender.visitUris(visitor);
- }
+ if (extras != null && extras.containsKey(WearableExtender.EXTRA_WEARABLE_EXTENSIONS)) {
+ WearableExtender extender = new WearableExtender(this);
+ extender.visitUris(visitor);
}
}
@@ -11459,16 +11428,6 @@
}
}
- private void visitUris(@NonNull Consumer<Uri> visitor) {
- visitIconUri(visitor, getIcon());
- if (mPendingIntent != null) {
- mPendingIntent.visitUris(visitor);
- }
- if (mDeleteIntent != null) {
- mDeleteIntent.visitUris(visitor);
- }
- }
-
/**
* Builder to construct a {@link BubbleMetadata} object.
*/
@@ -12667,9 +12626,6 @@
}
private void visitUris(@NonNull Consumer<Uri> visitor) {
- if (mDisplayIntent != null) {
- mDisplayIntent.visitUris(visitor);
- }
for (Action action : mActions) {
action.visitUris(visitor);
}
@@ -12829,12 +12785,6 @@
return mUnreadConversation;
}
- private void visitUris(@NonNull Consumer<Uri> visitor) {
- if (mUnreadConversation != null) {
- mUnreadConversation.visitUris(visitor);
- }
- }
-
/**
* A class which holds the unread messages from a conversation.
*/
@@ -12986,16 +12936,7 @@
onRead,
participants, b.getLong(KEY_TIMESTAMP));
}
-
- private void visitUris(@NonNull Consumer<Uri> visitor) {
- if (mReadPendingIntent != null) {
- mReadPendingIntent.visitUris(visitor);
- }
- if (mReplyPendingIntent != null) {
- mReplyPendingIntent.visitUris(visitor);
- }
- }
- }
+ };
/**
* Builder class for {@link CarExtender.UnreadConversation} objects.
@@ -13318,15 +13259,6 @@
public boolean isSuppressShowOverApps() {
return mSuppressShowOverApps;
}
-
- private void visitUris(@NonNull Consumer<Uri> visitor) {
- if (mContentIntent != null) {
- mContentIntent.visitUris(visitor);
- }
- if (mDeleteIntent != null) {
- mDeleteIntent.visitUris(visitor);
- }
- }
}
/**
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index e4310c1..bd80dc1 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -57,6 +57,7 @@
import android.service.notification.Adjustment;
import android.service.notification.Condition;
import android.service.notification.StatusBarNotification;
+import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenPolicy;
import android.util.Log;
@@ -1828,6 +1829,18 @@
throw e.rethrowFromSystemServer();
}
}
+ /**
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_MODES_UI)
+ public void setManualZenRuleDeviceEffects(@NonNull ZenDeviceEffects effects) {
+ INotificationManager service = getService();
+ try {
+ service.setManualZenRuleDeviceEffects(effects);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
/**
* For apps targeting {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} and above, the
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 1ac08ac..3714e5d 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -44,8 +44,6 @@
import android.content.pm.PackageManager.ResolveInfoFlagsBits;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
-import android.net.Uri;
-import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -71,7 +69,6 @@
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
-import java.util.function.Consumer;
/**
* A description of an Intent and target action to perform with it. Instances
@@ -1471,21 +1468,6 @@
return sb.toString();
}
- /**
- * See {@link Intent#visitUris(Consumer)}.
- *
- * @hide
- */
- public void visitUris(@NonNull Consumer<Uri> visitor) {
- if (android.app.Flags.visitRiskyUris()) {
- Intent intent = Binder.withCleanCallingIdentity(this::getIntent);
-
- if (intent != null) {
- intent.visitUris(visitor);
- }
- }
- }
-
/** @hide */
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
diff --git a/core/java/android/app/Person.java b/core/java/android/app/Person.java
index 041866001..96f6f4e 100644
--- a/core/java/android/app/Person.java
+++ b/core/java/android/app/Person.java
@@ -189,7 +189,7 @@
*/
public void visitUris(@NonNull Consumer<Uri> visitor) {
visitor.accept(getIconUri());
- if (Flags.visitRiskyUris()) {
+ if (Flags.visitPersonUri()) {
if (mUri != null && !mUri.isEmpty()) {
visitor.accept(Uri.parse(mUri));
}
diff --git a/core/java/android/app/assist/AssistContent.java b/core/java/android/app/assist/AssistContent.java
index e5316bc0..a488689 100644
--- a/core/java/android/app/assist/AssistContent.java
+++ b/core/java/android/app/assist/AssistContent.java
@@ -34,12 +34,22 @@
}
/**
+ * Create an AssistContent with extras initialized.
+ *
* @hide
+ */
+ public AssistContent(@android.annotation.NonNull Bundle extras) {
+ mExtras = extras;
+ }
+
+ /**
* Called by {@link android.app.ActivityThread} to set the default Intent based on
* {@link android.app.Activity#getIntent Activity.getIntent}.
*
* <p>Automatically populates {@link #mUri} if that Intent is an {@link Intent#ACTION_VIEW}
* of a web (http or https scheme) URI.</p>
+ *
+ * @hide
*/
public void setDefaultIntent(Intent intent) {
mIntent = intent;
diff --git a/core/java/android/app/contextualsearch/flags.aconfig b/core/java/android/app/contextualsearch/flags.aconfig
index 3385b2b..3b0c867 100644
--- a/core/java/android/app/contextualsearch/flags.aconfig
+++ b/core/java/android/app/contextualsearch/flags.aconfig
@@ -7,3 +7,9 @@
description: "Flag to enable the service"
bug: "309689654"
}
+flag {
+ name: "enable_token_refresh"
+ namespace: "machine_learning"
+ description: "Flag to refresh the token to the callback"
+ bug: "309689654"
+}
\ No newline at end of file
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 2d78317..f751a23 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -46,10 +46,13 @@
}
flag {
- name: "visit_risky_uris"
+ name: "visit_person_uri"
namespace: "systemui"
- description: "Guards the security fix that ensures all URIs in intents and Person.java are valid"
+ description: "Guards the security fix that ensures all URIs Person.java are valid"
bug: "281044385"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
# vvv Prototypes for using app icons in notifications vvv
@@ -199,3 +202,11 @@
description: "redacts notifications on the lockscreen if they have the 'sensitiveContent' flag"
bug: "343631648"
}
+
+flag {
+ name: "api_rich_ongoing"
+ is_exported: true
+ namespace: "systemui"
+ description: "Guards new android.app.richongoingnotification api"
+ bug: "337261753"
+}
\ No newline at end of file
diff --git a/core/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl b/core/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl
index 470b1ec..1977a39 100644
--- a/core/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl
+++ b/core/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl
@@ -24,6 +24,8 @@
import android.os.Bundle;
import android.app.ondeviceintelligence.Feature;
import android.app.ondeviceintelligence.FeatureDetails;
+ import android.app.ondeviceintelligence.InferenceInfo;
+ import java.util.List;
import android.app.ondeviceintelligence.IDownloadCallback;
import android.app.ondeviceintelligence.IListFeaturesCallback;
import android.app.ondeviceintelligence.IFeatureCallback;
@@ -72,4 +74,6 @@
in IStreamingResponseCallback streamingCallback) = 8;
String getRemoteServicePackageName() = 9;
+
+ List<InferenceInfo> getLatestInferenceInfo(long startTimeEpochMillis) = 10;
}
diff --git a/core/java/android/app/ondeviceintelligence/InferenceInfo.aidl b/core/java/android/app/ondeviceintelligence/InferenceInfo.aidl
new file mode 100644
index 0000000..6d70fc4
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/InferenceInfo.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ondeviceintelligence;
+
+/**
+ * @hide
+ */
+parcelable InferenceInfo;
diff --git a/core/java/android/app/ondeviceintelligence/InferenceInfo.java b/core/java/android/app/ondeviceintelligence/InferenceInfo.java
new file mode 100644
index 0000000..5557a81
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/InferenceInfo.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.ondeviceintelligence;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class represents the information related to an inference event to track the resource usage
+ * as a function of inference time.
+ *
+ * @hide
+ */
+public class InferenceInfo implements Parcelable {
+
+ /**
+ * Uid for the caller app.
+ */
+ private final int uid;
+
+ /**
+ * Inference start time (milliseconds from the epoch time).
+ */
+ private final long startTimeMs;
+
+ /**
+ * Inference end time (milliseconds from the epoch time).
+ */
+ private final long endTimeMs;
+
+ /**
+ * Suspended time in milliseconds.
+ */
+ private final long suspendedTimeMs;
+
+ /**
+ * Constructs an InferenceInfo object with the specified parameters.
+ *
+ * @param uid Uid for the caller app.
+ * @param startTimeMs Inference start time (milliseconds from the epoch time).
+ * @param endTimeMs Inference end time (milliseconds from the epoch time).
+ * @param suspendedTimeMs Suspended time in milliseconds.
+ */
+ public InferenceInfo(int uid, long startTimeMs, long endTimeMs,
+ long suspendedTimeMs) {
+ this.uid = uid;
+ this.startTimeMs = startTimeMs;
+ this.endTimeMs = endTimeMs;
+ this.suspendedTimeMs = suspendedTimeMs;
+ }
+
+ /**
+ * Constructs an InferenceInfo object from a Parcel.
+ *
+ * @param in The Parcel to read the object's data from.
+ */
+ protected InferenceInfo(Parcel in) {
+ uid = in.readInt();
+ startTimeMs = in.readLong();
+ endTimeMs = in.readLong();
+ suspendedTimeMs = in.readLong();
+ }
+
+
+ /**
+ * Writes the object's data to the provided Parcel.
+ *
+ * @param dest The Parcel to write the object's data to.
+ * @param flags Additional flags about how the object should be written.
+ */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(uid);
+ dest.writeLong(startTimeMs);
+ dest.writeLong(endTimeMs);
+ dest.writeLong(suspendedTimeMs);
+ }
+
+ /**
+ * Returns the UID for the caller app.
+ *
+ * @return the UID for the caller app.
+ */
+ public int getUid() {
+ return uid;
+ }
+
+ /**
+ * Returns the inference start time in milliseconds from the epoch time.
+ *
+ * @return the inference start time in milliseconds from the epoch time.
+ */
+ public long getStartTimeMs() {
+ return startTimeMs;
+ }
+
+ /**
+ * Returns the inference end time in milliseconds from the epoch time.
+ *
+ * @return the inference end time in milliseconds from the epoch time.
+ */
+ public long getEndTimeMs() {
+ return endTimeMs;
+ }
+
+ /**
+ * Returns the suspended time in milliseconds.
+ *
+ * @return the suspended time in milliseconds.
+ */
+ public long getSuspendedTimeMs() {
+ return suspendedTimeMs;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+
+ public static final @android.annotation.NonNull Parcelable.Creator<InferenceInfo> CREATOR
+ = new Parcelable.Creator<InferenceInfo>() {
+ @Override
+ public InferenceInfo[] newArray(int size) {
+ return new InferenceInfo[size];
+ }
+
+ @Override
+ public InferenceInfo createFromParcel(@android.annotation.NonNull Parcel in) {
+ return new InferenceInfo(in);
+ }
+ };
+
+ /**
+ * Builder class for creating instances of {@link InferenceInfo}.
+ */
+ public static class Builder {
+ private int uid;
+ private long startTimeMs;
+ private long endTimeMs;
+ private long suspendedTimeMs;
+
+ /**
+ * Sets the UID for the caller app.
+ *
+ * @param uid the UID for the caller app.
+ * @return the Builder instance.
+ */
+ public Builder setUid(int uid) {
+ this.uid = uid;
+ return this;
+ }
+
+ /**
+ * Sets the inference start time in milliseconds from the epoch time.
+ *
+ * @param startTimeMs the inference start time in milliseconds from the epoch time.
+ * @return the Builder instance.
+ */
+ public Builder setStartTimeMs(long startTimeMs) {
+ this.startTimeMs = startTimeMs;
+ return this;
+ }
+
+ /**
+ * Sets the inference end time in milliseconds from the epoch time.
+ *
+ * @param endTimeMs the inference end time in milliseconds from the epoch time.
+ * @return the Builder instance.
+ */
+ public Builder setEndTimeMs(long endTimeMs) {
+ this.endTimeMs = endTimeMs;
+ return this;
+ }
+
+ /**
+ * Sets the suspended time in milliseconds.
+ *
+ * @param suspendedTimeMs the suspended time in milliseconds.
+ * @return the Builder instance.
+ */
+ public Builder setSuspendedTimeMs(long suspendedTimeMs) {
+ this.suspendedTimeMs = suspendedTimeMs;
+ return this;
+ }
+
+ /**
+ * Builds and returns an instance of {@link InferenceInfo}.
+ *
+ * @return an instance of {@link InferenceInfo}.
+ */
+ public InferenceInfo build() {
+ return new InferenceInfo(uid, startTimeMs, endTimeMs,
+ suspendedTimeMs);
+ }
+ }
+}
diff --git a/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java b/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
index a37f51b..937a9cd 100644
--- a/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
+++ b/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
@@ -496,6 +496,24 @@
}
}
+ /**
+ * This is primarily intended to be used to attribute/blame on-device intelligence power usage,
+ * via the configured remote implementation, to its actual caller.
+ *
+ * @param startTimeEpochMillis epoch millis used to filter the InferenceInfo events.
+ * @return InferenceInfo events since the passed in startTimeEpochMillis.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.DUMP)
+ public List<InferenceInfo> getLatestInferenceInfo(long startTimeEpochMillis) {
+ try {
+ return mService.getLatestInferenceInfo(startTimeEpochMillis);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/** Request inference with provided Bundle and Params. */
public static final int REQUEST_TYPE_INFERENCE = 0;
diff --git a/core/java/android/app/wearable/WearableSensingManager.java b/core/java/android/app/wearable/WearableSensingManager.java
index b2b14ce..d28969d 100644
--- a/core/java/android/app/wearable/WearableSensingManager.java
+++ b/core/java/android/app/wearable/WearableSensingManager.java
@@ -449,7 +449,7 @@
* Consumer<android.service.voice.HotwordAudioStream>}, the system will check whether the {@link
* android.service.voice.VoiceInteractionService} at that time is {@code
* targetVisComponentName}. If not, the system will call {@link
- * WearableSensingService#onActiveHotwordAudioStopRequested()} and will not forward the audio
+ * WearableSensingService#onStopHotwordAudioStream()} and will not forward the audio
* data to the current {@link android.service.voice.HotwordDetectionService} nor {@link
* android.service.voice.VoiceInteractionService}. The system will not send a status code to
* {@code statusConsumer} regarding the {@code targetVisComponentName} check. The caller is
@@ -464,9 +464,9 @@
* continue to use the previous consumers after receiving a new one.
*
* <p>If the {@code statusConsumer} returns {@link STATUS_SUCCESS}, the caller should call
- * {@link #stopListeningForHotword(Executor, Consumer)} when it wants the wearable to stop
+ * {@link #stopHotwordRecognition(Executor, Consumer)} when it wants the wearable to stop
* listening for hotword. If the {@code statusConsumer} returns any other status code, a failure
- * has occurred and calling {@link #stopListeningForHotword(Executor, Consumer)} is not
+ * has occurred and calling {@link #stopHotwordRecognition(Executor, Consumer)} is not
* required. The system will not retry listening automatically. The caller should call this
* method again if they want to retry.
*
diff --git a/core/java/android/companion/ObservingDevicePresenceRequest.java b/core/java/android/companion/ObservingDevicePresenceRequest.java
index 11ea735..3150b87 100644
--- a/core/java/android/companion/ObservingDevicePresenceRequest.java
+++ b/core/java/android/companion/ObservingDevicePresenceRequest.java
@@ -180,6 +180,9 @@
* <p>Calling apps must use either this API or {@link #setAssociationId(int)},
* but not both.</p>
*
+ * <p>Calling app must hold the
+ * {@link AssociationRequest#DEVICE_PROFILE_AUTOMOTIVE_PROJECTION} profile.</p>
+ *
* @param uuid The ParcelUuid for observing device presence.
*/
@NonNull
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index bad73fc..4fcf6b6 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -7188,9 +7188,10 @@
* as the package remains in the foreground, or has any active manifest components (e.g. when
* another app is accessing a content provider in the package).
* <p>
- * If you want to revoke the permissions right away, you could call {@code System.exit()}, but
- * this could affect other apps that are accessing your app at the moment. For example, apps
- * accessing a content provider in your app will all crash.
+ * If you want to revoke the permissions right away, you could call {@code System.exit()} in
+ * {@code Handler.postDelayed} with a delay to allow completion of async IPC, But
+ * {@code System.exit()} could affect other apps that are accessing your app at the moment.
+ * For example, apps accessing a content provider in your app will all crash.
* <p>
* Note that the settings UI shows a permission group as granted as long as at least one
* permission in the group is granted. If you want the user to observe the revocation in the
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 02d62a2..2e60cb0 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -106,7 +106,6 @@
import java.util.Objects;
import java.util.Set;
import java.util.TimeZone;
-import java.util.function.Consumer;
/**
* An intent is an abstract description of an operation to be performed. It
@@ -8330,27 +8329,6 @@
}
}
- /**
- * Note all {@link Uri} that are referenced internally, with the expectation that Uri permission
- * grants will need to be issued to ensure the recipient of this object is able to render its
- * contents.
- * See b/281044385 for more context and examples about what happens when this isn't done
- * correctly.
- *
- * @hide
- */
- public void visitUris(@NonNull Consumer<Uri> visitor) {
- if (android.app.Flags.visitRiskyUris()) {
- visitor.accept(mData);
- if (mSelector != null) {
- mSelector.visitUris(visitor);
- }
- if (mOriginalIntent != null) {
- mOriginalIntent.visitUris(visitor);
- }
- }
- }
-
public static Intent getIntentOld(String uri) throws URISyntaxException {
Intent intent = getIntentOld(uri, 0);
intent.mLocalFlags |= LOCAL_FLAG_FROM_URI;
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 86d061c..e895d7b 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -823,6 +823,16 @@
}
/**
+ * Returns the number of actions in the filter, or {@code 0} if there are no actions.
+ * <p> This method provides a safe alternative to {@link #countActions()}, which
+ * may throw an exception if there are no actions.
+ * @hide
+ */
+ public final int safeCountActions() {
+ return mActions == null ? 0 : mActions.size();
+ }
+
+ /**
* Return an action in the filter.
*/
public final String getAction(int index) {
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 6168b68..df27f9d 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -695,15 +695,12 @@
* <p>If the caller is running on a managed profile, it'll return only the current profile.
* Otherwise it'll return the same list as {@link UserManager#getUserProfiles()} would.
*
- * <p>To get hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
- * caller should have either:</p>
- * <ul>
- * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL}
- * permission</li>
- * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the
- * {@link android.app.role.RoleManager.ROLE_HOME} role. </li>
- * </ul>
+ * <p>To get hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE},
+ * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
+ * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
*/
+ // Alternatively, a system app can access this api for private profile if they've been granted
+ // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
@SuppressLint("RequiresPermission")
@RequiresPermission(conditional = true,
anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
@@ -764,20 +761,17 @@
* list.</li>
* </ul>
*
- * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
- * caller should have either:</p>
- * <ul>
- * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL}
- * permission</li>
- * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the
- * {@link android.app.role.RoleManager.ROLE_HOME} role. </li>
- * </ul>
+ * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE},
+ * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
+ * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
*
* @param packageName The specific package to query. If null, it checks all installed packages
* in the profile.
* @param user The UserHandle of the profile.
* @return List of launchable activities. Can be an empty list but will not be null.
*/
+ // Alternatively, a system app can access this api for private profile if they've been granted
+ // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
@SuppressLint("RequiresPermission")
@RequiresPermission(conditional = true,
anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
@@ -820,19 +814,16 @@
* Returns information related to a user which is useful for displaying UI elements
* to distinguish it from other users (eg, badges).
*
- * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
- * caller should have either:</p>
- * <ul>
- * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL}
- * permission</li>
- * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the
- * {@link android.app.role.RoleManager.ROLE_HOME} role. </li>
- * </ul>
+ * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE},
+ * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
+ * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
*
* @param userHandle user handle of the user for which LauncherUserInfo is requested.
* @return the {@link LauncherUserInfo} object related to the user specified, null in case
* the user is inaccessible.
*/
+ // Alternatively, a system app can access this api for private profile if they've been granted
+ // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
@Nullable
@SuppressLint("RequiresPermission")
@FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
@@ -873,14 +864,9 @@
* </ul>
* </p>
*
- * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
- * caller should have either:</p>
- * <ul>
- * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL}
- * permission</li>
- * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the
- * {@link android.app.role.RoleManager.ROLE_HOME} role. </li>
- * </ul>
+ * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE},
+ * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
+ * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
*
* @param packageName the package for which intent sender to launch App Market Activity is
* required.
@@ -888,6 +874,8 @@
* @return {@link IntentSender} object which launches the App Market Activity, null in case
* there is no such activity.
*/
+ // Alternatively, a system app can access this api for private profile if they've been granted
+ // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
@Nullable
@SuppressLint("RequiresPermission")
@FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
@@ -913,19 +901,16 @@
* <p>An empty list denotes that all system packages should be treated as pre-installed for that
* user at creation.
*
- * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
- * caller should have either:</p>
- * <ul>
- * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL}
- * permission</li>
- * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the
- * {@link android.app.role.RoleManager.ROLE_HOME} role. </li>
- * </ul>
+ * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE},
+ * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
+ * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
*
* @param userHandle the user for which installed system packages are required.
* @return {@link List} of {@link String}, representing the package name of the installed
* package. Can be empty but not null.
*/
+ // Alternatively, a system app can access this api for private profile if they've been granted
+ // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
@FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
@NonNull
@SuppressLint("RequiresPermission")
@@ -945,13 +930,15 @@
/**
* Returns {@link IntentSender} which can be used to start the Private Space Settings Activity.
*
- * <p> Caller should have {@link android.app.role.RoleManager.ROLE_HOME} and either of the
+ * <p> Caller should have {@link android.app.role.RoleManager#ROLE_HOME} and either of the
* permissions required.</p>
*
* @return {@link IntentSender} object which launches the Private Space Settings Activity, if
* successful, null otherwise.
* @hide
*/
+ // Alternatively, a system app can access this api for private profile if they've been granted
+ // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
@Nullable
@FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
@RequiresPermission(conditional = true,
@@ -968,19 +955,16 @@
* Returns the activity info for a given intent and user handle, if it resolves. Otherwise it
* returns null.
*
- * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
- * caller should have either:</p>
- * <ul>
- * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL}
- * permission</li>
- * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the
- * {@link android.app.role.RoleManager.ROLE_HOME} role. </li>
- * </ul>
+ * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE},
+ * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
+ * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
*
* @param intent The intent to find a match for.
* @param user The profile to look in for a match.
* @return An activity info object if there is a match.
*/
+ // Alternatively, a system app can access this api for private profile if they've been granted
+ // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
@SuppressLint("RequiresPermission")
@RequiresPermission(conditional = true,
anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
@@ -1033,20 +1017,17 @@
/**
* Starts a Main activity in the specified profile.
*
- * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
- * caller should have either:</p>
- * <ul>
- * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL}
- * permission</li>
- * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the
- * {@link android.app.role.RoleManager.ROLE_HOME} role. </li>
- * </ul>
+ * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE},
+ * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
+ * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
*
* @param component The ComponentName of the activity to launch
* @param user The UserHandle of the profile
* @param sourceBounds The Rect containing the source bounds of the clicked icon
* @param opts Options to pass to startActivity
*/
+ // Alternatively, a system app can access this api for private profile if they've been granted
+ // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
@SuppressLint("RequiresPermission")
@RequiresPermission(conditional = true,
anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
@@ -1087,20 +1068,17 @@
* Starts the settings activity to show the application details for a
* package in the specified profile.
*
- * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
- * caller should have either:</p>
- * <ul>
- * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL}
- * permission</li>
- * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the
- * {@link android.app.role.RoleManager.ROLE_HOME} role. </li>
- * </ul>
+ * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE},
+ * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
+ * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
*
* @param component The ComponentName of the package to launch settings for.
* @param user The UserHandle of the profile
* @param sourceBounds The Rect containing the source bounds of the clicked icon
* @param opts Options to pass to startActivity
*/
+ // Alternatively, a system app can access this api for private profile if they've been granted
+ // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
@SuppressLint("RequiresPermission")
@RequiresPermission(conditional = true,
anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
@@ -1215,20 +1193,17 @@
/**
* Checks if the package is installed and enabled for a profile.
*
- * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
- * caller should have either:</p>
- * <ul>
- * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL}
- * permission</li>
- * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the
- * {@link android.app.role.RoleManager.ROLE_HOME} role. </li>
- * </ul>
+ * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE},
+ * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
+ * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
*
* @param packageName The package to check.
* @param user The UserHandle of the profile.
*
* @return true if the package exists and is enabled.
*/
+ // Alternatively, a system app can access this api for private profile if they've been granted
+ // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
@SuppressLint("RequiresPermission")
@RequiresPermission(conditional = true,
anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
@@ -1249,14 +1224,9 @@
* <p>The contents of this {@link Bundle} are supposed to be a contract between the suspending
* app and the launcher.
*
- * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
- * caller should have either:</p>
- * <ul>
- * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL}
- * permission</li>
- * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the
- * {@link android.app.role.RoleManager.ROLE_HOME} role. </li>
- * </ul>
+ * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE},
+ * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
+ * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
*
* <p>Note: This just returns whatever extras were provided to the system, <em>which might
* even be {@code null}.</em>
@@ -1269,6 +1239,8 @@
* @see Callback#onPackagesSuspended(String[], UserHandle, Bundle)
* @see PackageManager#isPackageSuspended()
*/
+ // Alternatively, a system app can access this api for private profile if they've been granted
+ // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
@SuppressLint("RequiresPermission")
@RequiresPermission(conditional = true,
anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
@@ -1286,19 +1258,16 @@
* could be done because the package was marked as distracting to the user via
* {@code PackageManager.setDistractingPackageRestrictions(String[], int)}.
*
- * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
- * caller should have either:</p>
- * <ul>
- * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL}
- * permission</li>
- * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the
- * {@link android.app.role.RoleManager.ROLE_HOME} role. </li>
- * </ul>
+ * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE},
+ * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
+ * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
*
* @param packageName The package for which to check.
* @param user the {@link UserHandle} of the profile.
* @return
*/
+ // Alternatively, a system app can access this api for private profile if they've been granted
+ // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
@SuppressLint("RequiresPermission")
@RequiresPermission(conditional = true,
anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
@@ -1316,14 +1285,9 @@
/**
* Returns {@link ApplicationInfo} about an application installed for a specific user profile.
*
- * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
- * caller should have either:</p>
- * <ul>
- * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL}
- * permission</li>
- * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the
- * {@link android.app.role.RoleManager.ROLE_HOME} role. </li>
- * </ul>
+ * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE},
+ * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
+ * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
*
* @param packageName The package name of the application
* @param flags Additional option flags {@link PackageManager#getApplicationInfo}
@@ -1333,6 +1297,8 @@
* {@code null} if the package isn't installed for the given profile, or the profile
* isn't enabled.
*/
+ // Alternatively, a system app can access this api for private profile if they've been granted
+ // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
@SuppressLint("RequiresPermission")
@RequiresPermission(conditional = true,
anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
@@ -1385,20 +1351,17 @@
* <p>The activity may still not be exported, in which case {@link #startMainActivity} will
* throw a {@link SecurityException} unless the caller has the same UID as the target app's.
*
- * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
- * caller should have either:</p>
- * <ul>
- * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL}
- * permission</li>
- * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the
- * {@link android.app.role.RoleManager.ROLE_HOME} role. </li>
- * </ul>
+ * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE},
+ * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
+ * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
*
* @param component The activity to check.
* @param user The UserHandle of the profile.
*
* @return true if the activity exists and is enabled.
*/
+ // Alternatively, a system app can access this api for private profile if they've been granted
+ // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
@SuppressLint("RequiresPermission")
@RequiresPermission(conditional = true,
anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
@@ -1960,17 +1923,14 @@
/**
* Registers a callback for changes to packages in this user and managed profiles.
*
- * <p>To receive callbacks for hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
- * caller should have either:</p>
- * <ul>
- * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL}
- * permission</li>
- * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the
- * {@link android.app.role.RoleManager.ROLE_HOME} role. </li>
- * </ul>
+ * <p>To receive callbacks for hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE},
+ * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
+ * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
*
* @param callback The callback to register.
*/
+ // Alternatively, a system app can access this api for private profile if they've been granted
+ // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
@SuppressLint("RequiresPermission")
@RequiresPermission(conditional = true,
anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
@@ -1981,18 +1941,15 @@
/**
* Registers a callback for changes to packages in this user and managed profiles.
*
- * <p>To receive callbacks for hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
- * caller should have either:</p>
- * <ul>
- * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL}
- * permission</li>
- * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the
- * {@link android.app.role.RoleManager.ROLE_HOME} role. </li>
- * </ul>
+ * <p>To receive callbacks for hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE},
+ * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
+ * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
*
* @param callback The callback to register.
* @param handler that should be used to post callbacks on, may be null.
*/
+ // Alternatively, a system app can access this api for private profile if they've been granted
+ // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
@SuppressLint("RequiresPermission")
@RequiresPermission(conditional = true,
anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
@@ -2446,14 +2403,9 @@
* package name in the app's manifest, have the android.permission.QUERY_ALL_PACKAGES, or be
* the session owner to retrieve these details.
*
- * <p>To receive callbacks for hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
- * caller should have either:</p>
- * <ul>
- * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES}
- * permission</li>
- * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the
- * {@link android.app.role.RoleManager.ROLE_HOME} role. </li>
- * </ul>
+ * <p>To receive callbacks for hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE},
+ * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
+ * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
*
* @see PackageInstaller#getAllSessions()
*/
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 821034a..c673d58 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -2797,6 +2797,8 @@
public int developmentInstallFlags = 0;
/** {@hide} */
public int unarchiveId = -1;
+ /** {@hide} */
+ public @Nullable String dexoptCompilerFilter = null;
private final ArrayMap<String, Integer> mPermissionStates;
@@ -2850,6 +2852,7 @@
applicationEnabledSettingPersistent = source.readBoolean();
developmentInstallFlags = source.readInt();
unarchiveId = source.readInt();
+ dexoptCompilerFilter = source.readString();
}
/** {@hide} */
@@ -2885,6 +2888,7 @@
ret.applicationEnabledSettingPersistent = applicationEnabledSettingPersistent;
ret.developmentInstallFlags = developmentInstallFlags;
ret.unarchiveId = unarchiveId;
+ ret.dexoptCompilerFilter = dexoptCompilerFilter;
return ret;
}
@@ -3564,6 +3568,11 @@
}
/** @hide */
+ public void setDexoptCompilerFilter(@Nullable String dexoptCompilerFilter) {
+ this.dexoptCompilerFilter = dexoptCompilerFilter;
+ }
+
+ /** @hide */
@NonNull
public ArrayMap<String, Integer> getPermissionStates() {
return mPermissionStates;
@@ -3622,6 +3631,7 @@
applicationEnabledSettingPersistent);
pw.printHexPair("developmentInstallFlags", developmentInstallFlags);
pw.printPair("unarchiveId", unarchiveId);
+ pw.printPair("dexoptCompilerFilter", dexoptCompilerFilter);
pw.println();
}
@@ -3667,6 +3677,7 @@
dest.writeBoolean(applicationEnabledSettingPersistent);
dest.writeInt(developmentInstallFlags);
dest.writeInt(unarchiveId);
+ dest.writeString(dexoptCompilerFilter);
}
public static final Parcelable.Creator<SessionParams>
diff --git a/core/java/android/database/DefaultDatabaseErrorHandler.java b/core/java/android/database/DefaultDatabaseErrorHandler.java
old mode 100755
new mode 100644
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 61f1ee1..37a2df8 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -38,6 +38,9 @@
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Binder;
@@ -193,7 +196,14 @@
@RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
@NonNull
public BiometricPrompt.Builder setLogoRes(@DrawableRes int logoRes) {
- mPromptInfo.setLogoRes(logoRes);
+ if (mPromptInfo.getLogoBitmap() != null) {
+ throw new IllegalStateException(
+ "Exclusively one of logo resource or logo bitmap can be set");
+ }
+ if (logoRes != 0) {
+ mPromptInfo.setLogo(logoRes,
+ convertDrawableToBitmap(mContext.getDrawable(logoRes)));
+ }
return this;
}
@@ -212,7 +222,11 @@
@RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
@NonNull
public BiometricPrompt.Builder setLogoBitmap(@NonNull Bitmap logoBitmap) {
- mPromptInfo.setLogoBitmap(logoBitmap);
+ if (mPromptInfo.getLogoRes() != 0) {
+ throw new IllegalStateException(
+ "Exclusively one of logo resource or logo bitmap can be set");
+ }
+ mPromptInfo.setLogo(0, logoBitmap);
return this;
}
@@ -821,7 +835,7 @@
* Gets the drawable resource of the logo for the prompt, as set by
* {@link Builder#setLogoRes(int)}. Currently for system applications use only.
*
- * @return The drawable resource of the logo, or -1 if the prompt has no logo resource set.
+ * @return The drawable resource of the logo, or 0 if the prompt has no logo resource set.
*/
@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
@RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
@@ -1516,4 +1530,29 @@
private static boolean isCredentialAllowed(@Authenticators.Types int allowedAuthenticators) {
return (allowedAuthenticators & Authenticators.DEVICE_CREDENTIAL) != 0;
}
+
+ /** Converts {@code drawable} to a {@link Bitmap}. */
+ private static Bitmap convertDrawableToBitmap(Drawable drawable) {
+ if (drawable == null) {
+ return null;
+ }
+
+ if (drawable instanceof BitmapDrawable) {
+ return ((BitmapDrawable) drawable).getBitmap();
+ }
+
+ Bitmap bitmap;
+ if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
+ bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+ // Single color bitmap will be created of 1x1 pixel
+ } else {
+ bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
+ drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
+ }
+
+ final Canvas canvas = new Canvas(bitmap);
+ drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+ drawable.draw(canvas);
+ return bitmap;
+ }
}
diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java
index bb07b9b..ba9f30d 100644
--- a/core/java/android/hardware/biometrics/PromptInfo.java
+++ b/core/java/android/hardware/biometrics/PromptInfo.java
@@ -33,7 +33,7 @@
*/
public class PromptInfo implements Parcelable {
- @DrawableRes private int mLogoRes = -1;
+ @DrawableRes private int mLogoRes;
@Nullable private Bitmap mLogoBitmap;
@Nullable private String mLogoDescription;
@NonNull private CharSequence mTitle;
@@ -187,7 +187,7 @@
* this permission.
*/
public boolean requiresAdvancedPermission() {
- if (mLogoRes != -1) {
+ if (mLogoRes != 0) {
return true;
} else if (mLogoBitmap != null) {
return true;
@@ -217,14 +217,17 @@
// LINT.ThenChange(frameworks/base/core/java/android/hardware/biometrics/BiometricPrompt.java)
// Setters
- public void setLogoRes(@DrawableRes int logoRes) {
- mLogoRes = logoRes;
- checkOnlyOneLogoSet();
- }
- public void setLogoBitmap(@NonNull Bitmap logoBitmap) {
+ /**
+ * Sets logo res and bitmap
+ *
+ * @param logoRes The logo res set by the app; Or 0 if the app sets bitmap directly.
+ * @param logoBitmap The bitmap from logoRes if the app sets logoRes; Or the bitmap set by the
+ * app directly.
+ */
+ public void setLogo(@DrawableRes int logoRes, @NonNull Bitmap logoBitmap) {
+ mLogoRes = logoRes;
mLogoBitmap = logoBitmap;
- checkOnlyOneLogoSet();
}
public void setLogoDescription(@NonNull String logoDescription) {
@@ -326,13 +329,29 @@
}
// Getters
+
+ /**
+ * Returns the logo bitmap either from logo resource or bitmap passed in from the app.
+ */
+ public Bitmap getLogo() {
+ return mLogoBitmap;
+ }
+
+ /**
+ * Returns the logo res set by the app.
+ */
@DrawableRes
public int getLogoRes() {
return mLogoRes;
}
+ /**
+ * Returns the logo bitmap set by the app.
+ */
public Bitmap getLogoBitmap() {
- return mLogoBitmap;
+ // If mLogoRes has been set, return null since mLogoBitmap is from the res, but not from
+ // the app directly.
+ return mLogoRes == 0 ? mLogoBitmap : null;
}
public String getLogoDescription() {
@@ -436,10 +455,4 @@
return mComponentNameForConfirmDeviceCredentialActivity;
}
- private void checkOnlyOneLogoSet() {
- if (mLogoRes != -1 && mLogoBitmap != null) {
- throw new IllegalStateException(
- "Exclusively one of logo resource or logo bitmap can be set");
- }
- }
}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 3cc87ea..6fffb82 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1472,9 +1472,9 @@
new Key<Integer>("android.flash.info.strengthDefaultLevel", int.class);
/**
- * <p>Maximum flash brightness level for manual flash control in SINGLE mode.</p>
+ * <p>Maximum flash brightness level for manual flash control in <code>SINGLE</code> mode.</p>
* <p>Maximum flash brightness level in camera capture mode and
- * {@link CaptureRequest#FLASH_MODE android.flash.mode} set to SINGLE.
+ * {@link CaptureRequest#FLASH_MODE android.flash.mode} set to <code>SINGLE</code>.
* Value will be > 1 if the manual flash strength control feature is supported,
* otherwise the value will be equal to 1.
* Note that this level is just a number of supported levels (the granularity of control).
@@ -1490,7 +1490,7 @@
new Key<Integer>("android.flash.singleStrengthMaxLevel", int.class);
/**
- * <p>Default flash brightness level for manual flash control in SINGLE mode.</p>
+ * <p>Default flash brightness level for manual flash control in <code>SINGLE</code> mode.</p>
* <p>If flash unit is available this will be greater than or equal to 1 and less
* or equal to {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel}.
* Note for devices that do not support the manual flash strength control
@@ -1506,9 +1506,9 @@
new Key<Integer>("android.flash.singleStrengthDefaultLevel", int.class);
/**
- * <p>Maximum flash brightness level for manual flash control in TORCH mode</p>
+ * <p>Maximum flash brightness level for manual flash control in <code>TORCH</code> mode</p>
* <p>Maximum flash brightness level in camera capture mode and
- * {@link CaptureRequest#FLASH_MODE android.flash.mode} set to TORCH.
+ * {@link CaptureRequest#FLASH_MODE android.flash.mode} set to <code>TORCH</code>.
* Value will be > 1 if the manual flash strength control feature is supported,
* otherwise the value will be equal to 1.</p>
* <p>Note that this level is just a number of supported levels(the granularity of control).
@@ -1530,7 +1530,7 @@
new Key<Integer>("android.flash.torchStrengthMaxLevel", int.class);
/**
- * <p>Default flash brightness level for manual flash control in TORCH mode</p>
+ * <p>Default flash brightness level for manual flash control in <code>TORCH</code> mode</p>
* <p>If flash unit is available this will be greater than or equal to 1 and less
* or equal to {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel}.
* Note for the devices that do not support the manual flash strength control feature,
@@ -4152,10 +4152,16 @@
/**
* <p>Whether the RAW images output from this camera device are subject to
* lens shading correction.</p>
- * <p>If TRUE, all images produced by the camera device in the RAW image formats will
- * have lens shading correction already applied to it. If FALSE, the images will
- * not be adjusted for lens shading correction.
- * See {@link CameraCharacteristics#REQUEST_MAX_NUM_OUTPUT_RAW android.request.maxNumOutputRaw} for a list of RAW image formats.</p>
+ * <p>If <code>true</code>, all images produced by the camera device in the <code>RAW</code> image formats will have
+ * at least some lens shading correction already applied to it. If <code>false</code>, the images will
+ * not be adjusted for lens shading correction. See {@link CameraCharacteristics#REQUEST_MAX_NUM_OUTPUT_RAW android.request.maxNumOutputRaw} for a
+ * list of RAW image formats.</p>
+ * <p>When <code>true</code>, the <code>lensShadingCorrectionMap</code> key may still have values greater than 1.0,
+ * and those will need to be applied to any captured RAW frames for them to match the shading
+ * correction of processed buffers such as <code>YUV</code> or <code>JPEG</code> images. This may occur, for
+ * example, when some basic fixed lens shading correction is applied by hardware to RAW data,
+ * and additional correction is done dynamically in the camera processing pipeline after
+ * demosaicing.</p>
* <p>This key will be <code>null</code> for all devices do not report this information.
* Devices with RAW capability will always report this information in this key.</p>
* <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index de26384..4819f67 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -2360,11 +2360,8 @@
* <p>If the session configuration is not supported, the AE mode reported in the
* CaptureResult will be 'ON' instead of 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY'.</p>
* <p>When this AE mode is enabled, the CaptureResult field
- * {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} will be present and not null. Otherwise, the
- * {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} field will not be present in the CaptureResult.</p>
- * <p>The application can observe the CaptureResult field
- * {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} to determine when low light boost is 'ACTIVE' or
- * 'INACTIVE'.</p>
+ * {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} will indicate when low light boost is 'ACTIVE'
+ * or 'INACTIVE'. By default {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} will be 'INACTIVE'.</p>
* <p>The low light boost is 'ACTIVE' once the scene lighting condition is less than the
* upper bound lux value defined by {@link CameraCharacteristics#CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE android.control.lowLightBoostInfoLuminanceRange}.
* This mode will be 'INACTIVE' once the scene lighting condition is greater than the
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 938636f..6968f27 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -2683,27 +2683,27 @@
/**
* <p>Flash strength level to be used when manual flash control is active.</p>
* <p>Flash strength level to use in capture mode i.e. when the applications control
- * flash with either SINGLE or TORCH mode.</p>
+ * flash with either <code>SINGLE</code> or <code>TORCH</code> mode.</p>
* <p>Use {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel} and
* {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel} to check whether the device supports
* flash strength control or not.
- * If the values of android.flash.info.singleStrengthMaxLevel and
+ * If the values of {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel} and
* {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel} are greater than 1,
* then the device supports manual flash strength control.</p>
- * <p>If the {@link CaptureRequest#FLASH_MODE android.flash.mode} <code>==</code> TORCH the value must be >= 1
+ * <p>If the {@link CaptureRequest#FLASH_MODE android.flash.mode} <code>==</code> <code>TORCH</code> the value must be >= 1
* and <= {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel}.
* If the application doesn't set the key and
* {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel} > 1,
* then the flash will be fired at the default level set by HAL in
* {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_DEFAULT_LEVEL android.flash.torchStrengthDefaultLevel}.
- * If the {@link CaptureRequest#FLASH_MODE android.flash.mode} <code>==</code> SINGLE, then the value must be >= 1
+ * If the {@link CaptureRequest#FLASH_MODE android.flash.mode} <code>==</code> <code>SINGLE</code>, then the value must be >= 1
* and <= {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel}.
* If the application does not set this key and
* {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel} > 1,
* then the flash will be fired at the default level set by HAL
* in {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL android.flash.singleStrengthDefaultLevel}.
- * If {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is set to any of ON_AUTO_FLASH, ON_ALWAYS_FLASH,
- * ON_AUTO_FLASH_REDEYE, ON_EXTERNAL_FLASH values, then the strengthLevel will be ignored.</p>
+ * If {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is set to any of <code>ON_AUTO_FLASH</code>, <code>ON_ALWAYS_FLASH</code>,
+ * <code>ON_AUTO_FLASH_REDEYE</code>, <code>ON_EXTERNAL_FLASH</code> values, then the strengthLevel will be ignored.</p>
* <p><b>Range of valid values:</b><br>
* <code>[1-{@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel}]</code> when the {@link CaptureRequest#FLASH_MODE android.flash.mode} is
* set to TORCH;
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 4406a41..d652b4c 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2819,12 +2819,11 @@
* <p>When low light boost is enabled by setting the AE mode to
* 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY', it can dynamically apply a low light
* boost when the light level threshold is exceeded.</p>
- * <p>This field is present in the CaptureResult when the AE mode is set to
- * 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY'. Otherwise, the field is not present.</p>
* <p>This state indicates when low light boost is 'ACTIVE' and applied. Similarly, it can
* indicate when it is not being applied by returning 'INACTIVE'.</p>
* <p>This key will be absent from the CaptureResult if AE mode is not set to
* 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY.</p>
+ * <p>The default value will always be 'INACTIVE'.</p>
* <p><b>Possible values:</b></p>
* <ul>
* <li>{@link #CONTROL_LOW_LIGHT_BOOST_STATE_INACTIVE INACTIVE}</li>
@@ -2976,27 +2975,27 @@
/**
* <p>Flash strength level to be used when manual flash control is active.</p>
* <p>Flash strength level to use in capture mode i.e. when the applications control
- * flash with either SINGLE or TORCH mode.</p>
+ * flash with either <code>SINGLE</code> or <code>TORCH</code> mode.</p>
* <p>Use {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel} and
* {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel} to check whether the device supports
* flash strength control or not.
- * If the values of android.flash.info.singleStrengthMaxLevel and
+ * If the values of {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel} and
* {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel} are greater than 1,
* then the device supports manual flash strength control.</p>
- * <p>If the {@link CaptureRequest#FLASH_MODE android.flash.mode} <code>==</code> TORCH the value must be >= 1
+ * <p>If the {@link CaptureRequest#FLASH_MODE android.flash.mode} <code>==</code> <code>TORCH</code> the value must be >= 1
* and <= {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel}.
* If the application doesn't set the key and
* {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel} > 1,
* then the flash will be fired at the default level set by HAL in
* {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_DEFAULT_LEVEL android.flash.torchStrengthDefaultLevel}.
- * If the {@link CaptureRequest#FLASH_MODE android.flash.mode} <code>==</code> SINGLE, then the value must be >= 1
+ * If the {@link CaptureRequest#FLASH_MODE android.flash.mode} <code>==</code> <code>SINGLE</code>, then the value must be >= 1
* and <= {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel}.
* If the application does not set this key and
* {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel} > 1,
* then the flash will be fired at the default level set by HAL
* in {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL android.flash.singleStrengthDefaultLevel}.
- * If {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is set to any of ON_AUTO_FLASH, ON_ALWAYS_FLASH,
- * ON_AUTO_FLASH_REDEYE, ON_EXTERNAL_FLASH values, then the strengthLevel will be ignored.</p>
+ * If {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is set to any of <code>ON_AUTO_FLASH</code>, <code>ON_ALWAYS_FLASH</code>,
+ * <code>ON_AUTO_FLASH_REDEYE</code>, <code>ON_EXTERNAL_FLASH</code> values, then the strengthLevel will be ignored.</p>
* <p><b>Range of valid values:</b><br>
* <code>[1-{@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel}]</code> when the {@link CaptureRequest#FLASH_MODE android.flash.mode} is
* set to TORCH;
@@ -4846,6 +4845,9 @@
* correction map that needs to be applied to get shading
* corrected images that match the camera device's output for
* non-RAW formats.</p>
+ * <p>Therefore, whatever the value of lensShadingApplied is, the lens
+ * shading map should always be applied to RAW images if the goal is to
+ * match the shading appearance of processed (non-RAW) images.</p>
* <p>For a complete shading correction map, the least shaded
* section of the image will have a gain factor of 1; all
* other sections will have gains above 1.</p>
diff --git a/core/java/android/hardware/camera2/ExtensionCaptureRequest.java b/core/java/android/hardware/camera2/ExtensionCaptureRequest.java
index c33956b..b681ce4 100644
--- a/core/java/android/hardware/camera2/ExtensionCaptureRequest.java
+++ b/core/java/android/hardware/camera2/ExtensionCaptureRequest.java
@@ -18,7 +18,6 @@
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
-import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureRequest.Key;
import android.hardware.camera2.impl.ExtensionKey;
@@ -43,6 +42,9 @@
@FlaggedApi(Flags.FLAG_CONCERT_MODE_API)
public final class ExtensionCaptureRequest {
+ /** To avoid exposing constructor */
+ private ExtensionCaptureRequest() {}
+
/**
* <p>Used to apply an additional digital zoom factor for the
* {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
diff --git a/core/java/android/hardware/camera2/ExtensionCaptureResult.java b/core/java/android/hardware/camera2/ExtensionCaptureResult.java
index 95feb2f..b7ba78c 100644
--- a/core/java/android/hardware/camera2/ExtensionCaptureResult.java
+++ b/core/java/android/hardware/camera2/ExtensionCaptureResult.java
@@ -18,7 +18,6 @@
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
-import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraExtensionCharacteristics;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.CaptureResult.Key;
@@ -45,6 +44,9 @@
@FlaggedApi(Flags.FLAG_CONCERT_MODE_API)
public final class ExtensionCaptureResult {
+ /** To avoid exposing constructor */
+ private ExtensionCaptureResult() {}
+
/**
* <p>The padding region for the
* {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java
index 8898a4c..df057a1 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java
@@ -105,7 +105,8 @@
try {
return cameraService.isSessionConfigurationWithParametersSupported(mCameraId,
- mTargetSdkVersion, config, mContext.getDeviceId(),
+ mTargetSdkVersion, config,
+ mContext.getDeviceId(),
mCameraManager.getDevicePolicyFromContext(mContext));
} catch (ServiceSpecificException e) {
throw ExceptionUtils.throwAsPublicException(e);
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index dda52dd..ebcc371 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -638,13 +638,15 @@
/**
* Create a list of {@link OutputConfiguration} instances for a
- * {@link android.hardware.camera2.params.MultiResolutionImageReader}.
+ * {@link MultiResolutionImageReader}.
*
* <p>This method can be used to create query OutputConfigurations for a
* MultiResolutionImageReader that can be included in a SessionConfiguration passed into
- * {@link CameraDeviceSetup#isSessionConfigurationSupported} before opening and setting up
- * a camera device in full, at which point {@link #setSurfacesForMultiResolutionOutput}
- * can be used to link to the actual MultiResolutionImageReader.</p>
+ * {@link
+ * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported}
+ * before opening and setting up a camera device in full, at which point {@link
+ * #setSurfacesForMultiResolutionOutput} can be used to link to the actual
+ * MultiResolutionImageReader.</p>
*
* <p>This constructor takes same arguments used to create a {@link
* MultiResolutionImageReader}: a collection of {@link MultiResolutionStreamInfo}
@@ -655,12 +657,12 @@
* @param format The format of the MultiResolutionImageReader. This must be one of the {@link
* android.graphics.ImageFormat} or {@link android.graphics.PixelFormat} constants
* supported by the camera device. Note that not all formats are supported, like
- * {@link ImageFormat.NV21}. The supported multi-resolution reader format can be
+ * {@link ImageFormat#NV21}. The supported multi-resolution reader format can be
* queried by {@link MultiResolutionStreamConfigurationMap#getOutputFormats}.
*
* @return The list of {@link OutputConfiguration} objects for a MultiResolutionImageReader.
*
- * @throws IllegaArgumentException If the {@code streams} is null or doesn't contain
+ * @throws IllegalArgumentException If the {@code streams} is null or doesn't contain
* at least 2 items, or if {@code format} isn't a valid camera
* format.
*
@@ -710,7 +712,7 @@
* instances.</p>
*
* @param outputConfigurations The OutputConfiguration objects created by {@link
- * #createInstancesFromMultiResolutionOutput}
+ * #createInstancesForMultiResolutionOutput}
* @param multiResolutionImageReader The MultiResolutionImageReader object created from the same
* MultiResolutionStreamInfo parameters as
* {@code outputConfigurations}.
@@ -759,31 +761,33 @@
* the deferred Surface can be obtained: (1) from {@link android.view.SurfaceView}
* by calling {@link android.view.SurfaceHolder#getSurface}, (2) from
* {@link android.graphics.SurfaceTexture} via
- * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}, (3) from {@link
- * android.media.MediaRecorder} via {@link android.media.MediaRecorder.getSurface} or {@link
- * android.media.MediaCodec#createPersistentInputSurface}, or (4) from {@link
- * android.media.MediaCodce} via {@link android.media.MediaCodec#createInputSurface} or {@link
- * android.media.MediaCodec#createPersistentInputSource}.</p>
+ * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}, (3) from
+ * {@link android.media.MediaRecorder} via {@link android.media.MediaRecorder#getSurface} or
+ * {@link android.media.MediaCodec#createPersistentInputSurface}, or (4) from
+ * {@link android.media.MediaCodec} via {@link android.media.MediaCodec#createInputSurface} or
+ * {@link android.media.MediaCodec#createPersistentInputSurface}.</p>
*
* <ul>
* <li>Surfaces for {@link android.view.SurfaceView} and {@link android.graphics.SurfaceTexture}
* can be deferred until after {@link CameraDevice#createCaptureSession}. In that case, the
* output Surface must be set via {@link #addSurface}, and the Surface configuration must be
- * finalized via {@link CameraCaptureSession#finalizeOutputConfiguration} before submitting
+ * finalized via {@link CameraCaptureSession#finalizeOutputConfigurations} before submitting
* a request with the Surface target.</li>
* <li>For all other target types, the output Surface must be set by {@link #addSurface},
- * and {@link CameraCaptureSession#finalizeOutputConfiguration} is not needed because the
+ * and {@link CameraCaptureSession#finalizeOutputConfigurations} is not needed because the
* OutputConfiguration used to create the session will contain the actual Surface.</li>
* </ul>
*
* <p>Before {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM Android V}, only {@link
* android.view.SurfaceView} and {@link android.graphics.SurfaceTexture} are supported. Both
* kind of outputs can be deferred until after {@link
- * CameraDevice#createCaptureSessionByOutputConfiguration}.</p>
+ * CameraDevice#createCaptureSessionByOutputConfigurations}.</p>
*
* <p>An OutputConfiguration object created by this constructor can be used for {@link
- * CameraDeviceSetup.isSessionConfigurationSupported} and {@link
- * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p>
+ * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported}
+ * and {@link
+ * android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics} without
+ * having called {@link #addSurface}.</p>
*
* @param surfaceSize Size for the deferred surface.
* @param klass a non-{@code null} {@link Class} object reference that indicates the source of
@@ -849,8 +853,10 @@
* before creating the capture session.</p>
*
* <p>An OutputConfiguration object created by this constructor can be used for {@link
- * CameraDeviceSetup.isSessionConfigurationSupported} and {@link
- * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p>
+ * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported}
+ * and {@link
+ * android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics} without
+ * having called {@link #addSurface}.</p>
*
* @param format The format of the ImageReader output. This must be one of the
* {@link android.graphics.ImageFormat} or {@link android.graphics.PixelFormat}
@@ -873,8 +879,10 @@
* before creating the capture session.</p>
*
* <p>An OutputConfiguration object created by this constructor can be used for {@link
- * CameraDeviceSetup.isSessionConfigurationSupported} and {@link
- * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p>
+ * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported}
+ * and {@link
+ * android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics} without
+ * having called {@link #addSurface}.</p>
*
* @param surfaceGroupId A group ID for this output, used for sharing memory between multiple
* outputs.
@@ -899,8 +907,10 @@
* before creating the capture session.</p>
*
* <p>An OutputConfiguration object created by this constructor can be used for {@link
- * CameraDeviceSetup.isSessionConfigurationSupported} and {@link
- * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p>
+ * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported}
+ * and {@link
+ * android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics} without
+ * having called {@link #addSurface}.</p>
*
* @param format The format of the ImageReader output. This must be one of the
* {@link android.graphics.ImageFormat} or {@link android.graphics.PixelFormat}
@@ -923,8 +933,10 @@
* before creating the capture session.</p>
*
* <p>An OutputConfiguration object created by this constructor can be used for {@link
- * CameraDeviceSetup.isSessionConfigurationSupported} and {@link
- * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p>
+ * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported}
+ * and {@link
+ * android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics} without
+ * having called {@link #addSurface}.</p>
*
* @param surfaceGroupId A group ID for this output, used for sharing memory between multiple
* outputs.
@@ -1171,9 +1183,9 @@
* <li>from {@link android.media.MediaRecorder} by calling
* {@link android.media.MediaRecorder#getSurface} or {@link
* android.media.MediaCodec#createPersistentInputSurface}</li>
- * <li>from {@link android.media.MediaCodce} by calling
- * {@link android.media.MediaCodec#createInputSurface} or {@link
- * android.media.MediaCodec#createPersistentInputSource}</li>
+ * <li>from {@link android.media.MediaCodec} by calling
+ * {@link android.media.MediaCodec#createInputSurface} or
+ * {@link android.media.MediaCodec#createPersistentInputSurface()}</li>
* </ul>
*
* <p> If the OutputConfiguration was constructed by {@link #OutputConfiguration(int, Size)}
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 3d7b714..8519722 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -548,6 +548,20 @@
}
}
+ /**
+ * Request to power a display ON or OFF.
+ * @hide
+ */
+ @RequiresPermission("android.permission.MANAGE_DISPLAYS")
+ public boolean requestDisplayPower(int displayId, boolean on) {
+ try {
+ return mDm.requestDisplayPower(displayId, on);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Error trying to request display power " + on, ex);
+ return false;
+ }
+ }
+
public void startWifiDisplayScan() {
synchronized (mLock) {
if (mWifiDisplayScanNestCount++ == 0) {
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 70efc6f..b7c02b0 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -236,6 +236,10 @@
@EnforcePermission("MANAGE_DISPLAYS")
void disableConnectedDisplay(int displayId);
+ // Request to power display ON or OFF.
+ @EnforcePermission("MANAGE_DISPLAYS")
+ boolean requestDisplayPower(int displayId, boolean on);
+
// Restricts display modes to specified modeIds.
@EnforcePermission("RESTRICT_DISPLAY_MODES")
void requestDisplayModes(in IBinder token, int displayId, in @nullable int[] modeIds);
diff --git a/core/java/android/hardware/location/ISignificantPlaceProvider.aidl b/core/java/android/hardware/location/ISignificantPlaceProvider.aidl
index e02169e..992dbff 100644
--- a/core/java/android/hardware/location/ISignificantPlaceProvider.aidl
+++ b/core/java/android/hardware/location/ISignificantPlaceProvider.aidl
@@ -7,4 +7,5 @@
*/
oneway interface ISignificantPlaceProvider {
void setSignificantPlaceProviderManager(in ISignificantPlaceProviderManager manager);
+ void onSignificantPlaceCheck();
}
diff --git a/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig b/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig
index 967fc42..0f944cf 100644
--- a/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig
+++ b/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig
@@ -28,4 +28,11 @@
namespace: "preload_safety"
description: "Enables signal API with staking"
bug: "287498482"
-}
\ No newline at end of file
+}
+
+flag {
+ name: "enable_usb_sysfs_midi_identification"
+ namespace: "system_sw_usb"
+ description: "Enable identifying midi device using USB sysfs"
+ bug: "333778731"
+}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 278e863..943b04f 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -401,10 +401,7 @@
private long mStylusHwSessionsTimeout = STYLUS_HANDWRITING_IDLE_TIMEOUT_MS;
private Runnable mStylusWindowIdleTimeoutRunnable;
private long mStylusWindowIdleTimeoutForTest;
- /**
- * Tracks last {@link MotionEvent#getToolType(int)} used for {@link MotionEvent#ACTION_DOWN}.
- **/
- private int mLastUsedToolType;
+
/**
* Tracks the ctrl+shift shortcut
**/
@@ -720,6 +717,7 @@
final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = info -> {
onComputeInsets(mTmpInsets);
+ mNavigationBarController.updateInsets(mTmpInsets);
if (!mViewsCreated) {
// The IME views are not ready, keep visible insets untouched.
mTmpInsets.visibleTopInsets = 0;
@@ -1367,7 +1365,6 @@
private void updateEditorToolTypeInternal(int toolType) {
if (Flags.useHandwritingListenerForTooltype()) {
- mLastUsedToolType = toolType;
if (mInputEditorInfo != null) {
mInputEditorInfo.setInitialToolType(toolType);
}
@@ -3384,9 +3381,6 @@
null /* icProto */);
mInputStarted = true;
mStartedInputConnection = ic;
- if (Flags.useHandwritingListenerForTooltype()) {
- editorInfo.setInitialToolType(mLastUsedToolType);
- }
mInputEditorInfo = editorInfo;
initialize();
mInlineSuggestionSessionController.notifyOnStartInput(
diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java
index 9c55b0e..de67e06 100644
--- a/core/java/android/inputmethodservice/NavigationBarController.java
+++ b/core/java/android/inputmethodservice/NavigationBarController.java
@@ -58,6 +58,10 @@
final class NavigationBarController {
private interface Callback {
+
+ default void updateInsets(@NonNull InputMethodService.Insets originalInsets) {
+ }
+
default void updateTouchableInsets(@NonNull InputMethodService.Insets originalInsets,
@NonNull ViewTreeObserver.InternalInsetsInfo dest) {
}
@@ -96,6 +100,15 @@
? new Impl(inputMethodService) : Callback.NOOP;
}
+ /**
+ * Update the given insets to be at least as big as the IME navigation bar, when visible.
+ *
+ * @param originalInsets the insets to check and modify to include the IME navigation bar.
+ */
+ void updateInsets(@NonNull InputMethodService.Insets originalInsets) {
+ mImpl.updateInsets(originalInsets);
+ }
+
void updateTouchableInsets(@NonNull InputMethodService.Insets originalInsets,
@NonNull ViewTreeObserver.InternalInsetsInfo dest) {
mImpl.updateTouchableInsets(originalInsets, dest);
@@ -270,6 +283,24 @@
}
@Override
+ public void updateInsets(@NonNull InputMethodService.Insets originalInsets) {
+ if (!mImeDrawsImeNavBar || mNavigationBarFrame == null
+ || mNavigationBarFrame.getVisibility() != View.VISIBLE
+ || mService.isFullscreenMode()) {
+ return;
+ }
+
+ final int[] loc = new int[2];
+ mNavigationBarFrame.getLocationInWindow(loc);
+ if (originalInsets.contentTopInsets > loc[1]) {
+ originalInsets.contentTopInsets = loc[1];
+ }
+ if (originalInsets.visibleTopInsets > loc[1]) {
+ originalInsets.visibleTopInsets = loc[1];
+ }
+ }
+
+ @Override
public void updateTouchableInsets(@NonNull InputMethodService.Insets originalInsets,
@NonNull ViewTreeObserver.InternalInsetsInfo dest) {
if (!mImeDrawsImeNavBar || mNavigationBarFrame == null) {
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index 7bdd53d..02f3a25 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -200,6 +200,8 @@
POWER_COMPONENT_AUDIO,
POWER_COMPONENT_VIDEO,
POWER_COMPONENT_FLASHLIGHT,
+ POWER_COMPONENT_CAMERA,
+ POWER_COMPONENT_GNSS,
};
static final int COLUMN_INDEX_BATTERY_CONSUMER_TYPE = 0;
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 3e6223a..065b3d6 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1994,7 +1994,8 @@
// STATES2 bits that are used for Power Stats tracking
public static final int IMPORTANT_FOR_POWER_STATS_STATES2 =
- STATE2_VIDEO_ON_FLAG | STATE2_FLASHLIGHT_FLAG | STATE2_CAMERA_FLAG;
+ STATE2_VIDEO_ON_FLAG | STATE2_FLASHLIGHT_FLAG | STATE2_CAMERA_FLAG
+ | STATE2_GPS_SIGNAL_QUALITY_MASK;
@UnsupportedAppUsage
public int states2;
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
old mode 100755
new mode 100644
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index fdce476..20522fa 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1907,6 +1907,31 @@
"no_near_field_communication_radio";
/**
+ * This user restriction specifies if Near-field communication is disallowed to change
+ * on the device. If Near-field communication is disallowed it cannot be changed via Settings.
+ *
+ * <p>This restriction can only be set by a device owner or a profile owner of an
+ * organization-owned managed profile on the parent profile.
+ * In both cases, the restriction applies globally on the device and will not allow Near-field
+ * communication state being changed.
+ *
+ * <p>
+ * Near-field communication (NFC) is a radio technology that allows two devices (like your phone
+ * and a payments terminal) to communicate with each other when they're close together.
+ *
+ * <p>Default is <code>false</code>.
+ *
+ * <p>Key for user restrictions.
+ * <p>Type: Boolean
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+ * @see #getUserRestrictions()
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_USER_RESTRICTION)
+ public static final String DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO =
+ "no_change_near_field_communication_radio";
+
+ /**
* This user restriction specifies if Thread network is disallowed on the device. If Thread
* network is disallowed it cannot be turned on via Settings.
*
@@ -2007,6 +2032,7 @@
DISALLOW_CAMERA,
DISALLOW_CAMERA_TOGGLE,
DISALLOW_CELLULAR_2G,
+ DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO,
DISALLOW_CHANGE_WIFI_STATE,
DISALLOW_CONFIG_BLUETOOTH,
DISALLOW_CONFIG_BRIGHTNESS,
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 05345d8..63f0b9e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6101,6 +6101,15 @@
public static final String POINTER_SPEED = "pointer_speed";
/**
+ * Pointer scale setting.
+ *
+ * <p>This float value represents the scale by which the size of the pointer increases.
+ * @hide
+ */
+ @Readable
+ public static final String POINTER_SCALE = "pointer_scale";
+
+ /**
* Touchpad pointer speed setting.
* This is an integer value in a range between -7 and +7, so there are 15 possible values.
* -7 = slowest
@@ -6358,6 +6367,7 @@
PRIVATE_SETTINGS.add(SIP_ASK_ME_EACH_TIME);
PRIVATE_SETTINGS.add(POINTER_SPEED);
PRIVATE_SETTINGS.add(POINTER_FILL_STYLE);
+ PRIVATE_SETTINGS.add(POINTER_SCALE);
PRIVATE_SETTINGS.add(LOCK_TO_APP_ENABLED);
PRIVATE_SETTINGS.add(EGG_MODE);
PRIVATE_SETTINGS.add(SHOW_BATTERY_PERCENT);
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index e1965ef..405fe26 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -29,6 +29,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.Service;
+import android.app.assist.AssistContent;
import android.content.ComponentName;
import android.content.ContentCaptureOptions;
import android.content.Intent;
@@ -133,6 +134,16 @@
*/
public static final String SERVICE_META_DATA = "android.content_capture";
+
+ /**
+ * Extras key to flag that the passed in {@link AssistContent} is sent only during Activity
+ * start.
+ *
+ * @hide
+ */
+ public static final String ASSIST_CONTENT_ACTIVITY_START_KEY = "activity_start_assist_content";
+
+
private final LocalDataShareAdapterResourceManager mDataShareAdapterResourceManager =
new LocalDataShareAdapterResourceManager();
diff --git a/core/java/android/service/notification/ZenAdapters.java b/core/java/android/service/notification/ZenAdapters.java
index b249815..a122b71 100644
--- a/core/java/android/service/notification/ZenAdapters.java
+++ b/core/java/android/service/notification/ZenAdapters.java
@@ -33,7 +33,7 @@
.allowAlarms(policy.allowAlarms())
.allowCalls(
policy.allowCalls()
- ? notificationPolicySendersToZenPolicyPeopleType(
+ ? prioritySendersToPeopleType(
policy.allowCallsFrom())
: ZenPolicy.PEOPLE_TYPE_NONE)
.allowConversations(
@@ -45,7 +45,7 @@
.allowMedia(policy.allowMedia())
.allowMessages(
policy.allowMessages()
- ? notificationPolicySendersToZenPolicyPeopleType(
+ ? prioritySendersToPeopleType(
policy.allowMessagesFrom())
: ZenPolicy.PEOPLE_TYPE_NONE)
.allowReminders(policy.allowReminders())
@@ -71,7 +71,7 @@
/** Maps {@link ZenPolicy.PeopleType} enum to {@link Policy.PrioritySenders}. */
@Policy.PrioritySenders
- public static int zenPolicyPeopleTypeToNotificationPolicySenders(
+ public static int peopleTypeToPrioritySenders(
@ZenPolicy.PeopleType int zpPeopleType, @Policy.PrioritySenders int defaultResult) {
switch (zpPeopleType) {
case ZenPolicy.PEOPLE_TYPE_ANYONE:
@@ -87,7 +87,7 @@
/** Maps {@link Policy.PrioritySenders} enum to {@link ZenPolicy.PeopleType}. */
@ZenPolicy.PeopleType
- public static int notificationPolicySendersToZenPolicyPeopleType(
+ public static int prioritySendersToPeopleType(
@Policy.PrioritySenders int npPrioritySenders) {
switch (npPrioritySenders) {
case Policy.PRIORITY_SENDERS_ANY:
diff --git a/core/java/android/service/notification/ZenDeviceEffects.aidl b/core/java/android/service/notification/ZenDeviceEffects.aidl
new file mode 100644
index 0000000..e635493
--- /dev/null
+++ b/core/java/android/service/notification/ZenDeviceEffects.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2024, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.notification;
+
+parcelable ZenDeviceEffects;
+
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 610a317..7a0c016 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -24,9 +24,26 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
-import static android.service.notification.ZenAdapters.notificationPolicySendersToZenPolicyPeopleType;
+import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.service.notification.ZenAdapters.peopleTypeToPrioritySenders;
+import static android.service.notification.ZenAdapters.prioritySendersToPeopleType;
import static android.service.notification.ZenAdapters.zenPolicyConversationSendersToNotificationPolicy;
-import static android.service.notification.ZenAdapters.zenPolicyPeopleTypeToNotificationPolicySenders;
+import static android.service.notification.ZenModeConfig.EventInfo.REPLY_YES_OR_MAYBE;
+import static android.service.notification.ZenPolicy.PEOPLE_TYPE_STARRED;
+import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_ALARMS;
+import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_CALLS;
+import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_EVENTS;
+import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_MEDIA;
+import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_MESSAGES;
+import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_REMINDERS;
+import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS;
+import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_SYSTEM;
+import static android.service.notification.ZenPolicy.STATE_ALLOW;
+import static android.service.notification.ZenPolicy.STATE_DISALLOW;
+import static android.service.notification.ZenPolicy.VISUAL_EFFECT_AMBIENT;
+import static android.service.notification.ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT;
+import static android.service.notification.ZenPolicy.VISUAL_EFFECT_LIGHTS;
+import static android.service.notification.ZenPolicy.VISUAL_EFFECT_PEEK;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
@@ -58,6 +75,7 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
@@ -320,31 +338,107 @@
@UnsupportedAppUsage
public ZenModeConfig() {
+ if (Flags.modesUi()) {
+ ensureManualZenRule();
+ }
}
public ZenModeConfig(Parcel source) {
- allowCalls = source.readInt() == 1;
- allowRepeatCallers = source.readInt() == 1;
- allowMessages = source.readInt() == 1;
- allowReminders = source.readInt() == 1;
- allowEvents = source.readInt() == 1;
- allowCallsFrom = source.readInt();
- allowMessagesFrom = source.readInt();
+ if (!Flags.modesUi()) {
+ allowCalls = source.readInt() == 1;
+ allowRepeatCallers = source.readInt() == 1;
+ allowMessages = source.readInt() == 1;
+ allowReminders = source.readInt() == 1;
+ allowEvents = source.readInt() == 1;
+ allowCallsFrom = source.readInt();
+ allowMessagesFrom = source.readInt();
+ }
user = source.readInt();
manualRule = source.readParcelable(null, ZenRule.class);
readRulesFromParcel(automaticRules, source);
if (Flags.modesApi()) {
readRulesFromParcel(deletedRules, source);
}
- allowAlarms = source.readInt() == 1;
- allowMedia = source.readInt() == 1;
- allowSystem = source.readInt() == 1;
- suppressedVisualEffects = source.readInt();
+ if (!Flags.modesUi()) {
+ allowAlarms = source.readInt() == 1;
+ allowMedia = source.readInt() == 1;
+ allowSystem = source.readInt() == 1;
+ suppressedVisualEffects = source.readInt();
+ }
areChannelsBypassingDnd = source.readInt() == 1;
- allowConversations = source.readBoolean();
- allowConversationsFrom = source.readInt();
- if (Flags.modesApi()) {
- allowPriorityChannels = source.readBoolean();
+ if (!Flags.modesUi()) {
+ allowConversations = source.readBoolean();
+ allowConversationsFrom = source.readInt();
+ if (Flags.modesApi()) {
+ allowPriorityChannels = source.readBoolean();
+ }
+ }
+ }
+
+ public static ZenPolicy getDefaultZenPolicy() {
+ ZenPolicy policy = new ZenPolicy.Builder()
+ .allowAlarms(true)
+ .allowMedia(true)
+ .allowSystem(false)
+ .allowCalls(PEOPLE_TYPE_STARRED)
+ .allowMessages(PEOPLE_TYPE_STARRED)
+ .allowReminders(false)
+ .allowEvents(false)
+ .allowRepeatCallers(true)
+ .allowConversations(CONVERSATION_SENDERS_IMPORTANT)
+ .showAllVisualEffects()
+ .showVisualEffect(VISUAL_EFFECT_FULL_SCREEN_INTENT, false)
+ .showVisualEffect(VISUAL_EFFECT_LIGHTS, false)
+ .showVisualEffect(VISUAL_EFFECT_PEEK, false)
+ .showVisualEffect(VISUAL_EFFECT_AMBIENT, false)
+ .allowPriorityChannels(true)
+ .build();
+ return policy;
+ }
+
+ public static ZenModeConfig getDefaultConfig() {
+ ZenModeConfig config = new ZenModeConfig();
+
+ EventInfo eventInfo = new EventInfo();
+ eventInfo.reply = REPLY_YES_OR_MAYBE;
+ ZenRule events = new ZenRule();
+ events.id = EVENTS_DEFAULT_RULE_ID;
+ events.conditionId = toEventConditionId(eventInfo);
+ events.component = ComponentName.unflattenFromString(
+ "android/com.android.server.notification.EventConditionProvider");
+ events.enabled = false;
+ events.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ events.pkg = "android";
+ config.automaticRules.put(EVENTS_DEFAULT_RULE_ID, events);
+
+ ScheduleInfo scheduleInfo = new ScheduleInfo();
+ scheduleInfo.days = new int[] {1, 2, 3, 4, 5, 6, 7};
+ scheduleInfo.startHour = 22;
+ scheduleInfo.endHour = 7;
+ scheduleInfo.exitAtAlarm = true;
+ ZenRule sleeping = new ZenRule();
+ sleeping.id = EVERY_NIGHT_DEFAULT_RULE_ID;
+ sleeping.conditionId = toScheduleConditionId(scheduleInfo);
+ sleeping.component = ComponentName.unflattenFromString(
+ "android/com.android.server.notification.ScheduleConditionProvider");
+ sleeping.enabled = false;
+ sleeping.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ sleeping.pkg = "android";
+ config.automaticRules.put(EVERY_NIGHT_DEFAULT_RULE_ID, sleeping);
+
+ return config;
+ }
+
+ void ensureManualZenRule() {
+ if (manualRule == null) {
+ final ZenRule newRule = new ZenRule();
+ newRule.type = AutomaticZenRule.TYPE_OTHER;
+ newRule.enabled = true;
+ newRule.conditionId = Uri.EMPTY;
+ newRule.allowManualInvocation = true;
+ newRule.zenPolicy = getDefaultZenPolicy();
+ newRule.pkg = "android";
+ manualRule = newRule;
}
}
@@ -363,28 +457,34 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(allowCalls ? 1 : 0);
- dest.writeInt(allowRepeatCallers ? 1 : 0);
- dest.writeInt(allowMessages ? 1 : 0);
- dest.writeInt(allowReminders ? 1 : 0);
- dest.writeInt(allowEvents ? 1 : 0);
- dest.writeInt(allowCallsFrom);
- dest.writeInt(allowMessagesFrom);
+ if (!Flags.modesUi()) {
+ dest.writeInt(allowCalls ? 1 : 0);
+ dest.writeInt(allowRepeatCallers ? 1 : 0);
+ dest.writeInt(allowMessages ? 1 : 0);
+ dest.writeInt(allowReminders ? 1 : 0);
+ dest.writeInt(allowEvents ? 1 : 0);
+ dest.writeInt(allowCallsFrom);
+ dest.writeInt(allowMessagesFrom);
+ }
dest.writeInt(user);
dest.writeParcelable(manualRule, 0);
writeRulesToParcel(automaticRules, dest);
if (Flags.modesApi()) {
writeRulesToParcel(deletedRules, dest);
}
- dest.writeInt(allowAlarms ? 1 : 0);
- dest.writeInt(allowMedia ? 1 : 0);
- dest.writeInt(allowSystem ? 1 : 0);
- dest.writeInt(suppressedVisualEffects);
+ if (!Flags.modesUi()) {
+ dest.writeInt(allowAlarms ? 1 : 0);
+ dest.writeInt(allowMedia ? 1 : 0);
+ dest.writeInt(allowSystem ? 1 : 0);
+ dest.writeInt(suppressedVisualEffects);
+ }
dest.writeInt(areChannelsBypassingDnd ? 1 : 0);
- dest.writeBoolean(allowConversations);
- dest.writeInt(allowConversationsFrom);
- if (Flags.modesApi()) {
- dest.writeBoolean(allowPriorityChannels);
+ if (!Flags.modesUi()) {
+ dest.writeBoolean(allowConversations);
+ dest.writeInt(allowConversationsFrom);
+ if (Flags.modesApi()) {
+ dest.writeBoolean(allowPriorityChannels);
+ }
}
}
@@ -408,35 +508,251 @@
@Override
public String toString() {
StringBuilder sb = new StringBuilder(ZenModeConfig.class.getSimpleName()).append('[')
- .append("user=").append(user)
- .append(",allowAlarms=").append(allowAlarms)
- .append(",allowMedia=").append(allowMedia)
- .append(",allowSystem=").append(allowSystem)
- .append(",allowReminders=").append(allowReminders)
- .append(",allowEvents=").append(allowEvents)
- .append(",allowCalls=").append(allowCalls)
- .append(",allowRepeatCallers=").append(allowRepeatCallers)
- .append(",allowMessages=").append(allowMessages)
- .append(",allowConversations=").append(allowConversations)
- .append(",allowCallsFrom=").append(sourceToString(allowCallsFrom))
- .append(",allowMessagesFrom=").append(sourceToString(allowMessagesFrom))
- .append(",allowConvFrom=").append(ZenPolicy.conversationTypeToString
- (allowConversationsFrom))
- .append(",suppressedVisualEffects=").append(suppressedVisualEffects);
+ .append("user=").append(user);
+ if (!Flags.modesUi()) {
+ sb.append(",allowAlarms=").append(allowAlarms)
+ .append(",allowMedia=").append(allowMedia)
+ .append(",allowSystem=").append(allowSystem)
+ .append(",allowReminders=").append(allowReminders)
+ .append(",allowEvents=").append(allowEvents)
+ .append(",allowCalls=").append(allowCalls)
+ .append(",allowRepeatCallers=").append(allowRepeatCallers)
+ .append(",allowMessages=").append(allowMessages)
+ .append(",allowConversations=").append(allowConversations)
+ .append(",allowCallsFrom=").append(sourceToString(allowCallsFrom))
+ .append(",allowMessagesFrom=").append(sourceToString(allowMessagesFrom))
+ .append(",allowConvFrom=").append(ZenPolicy.conversationTypeToString
+ (allowConversationsFrom))
+ .append("\nsuppressedVisualEffects=").append(suppressedVisualEffects);
+ }
if (Flags.modesApi()) {
- sb.append(",hasPriorityChannels=").append(areChannelsBypassingDnd);
+ sb.append("\nhasPriorityChannels=").append(areChannelsBypassingDnd);
sb.append(",allowPriorityChannels=").append(allowPriorityChannels);
} else {
- sb.append(",areChannelsBypassingDnd=").append(areChannelsBypassingDnd);
+ sb.append("\nareChannelsBypassingDnd=").append(areChannelsBypassingDnd);
}
- sb.append(",\nautomaticRules=").append(rulesToString(automaticRules))
- .append(",\nmanualRule=").append(manualRule);
+ sb.append(",\nautomaticRules=").append(rulesToString(automaticRules));
+ sb.append(",\nmanualRule=").append(manualRule);
if (Flags.modesApi()) {
sb.append(",\ndeletedRules=").append(rulesToString(deletedRules));
}
return sb.append(']').toString();
}
+ public boolean isAllowPriorityChannels() {
+ if (Flags.modesUi()) {
+ throw new IllegalStateException("can't be used with modesUI flag");
+ }
+ return allowPriorityChannels;
+ }
+
+ public void setAllowPriorityChannels(boolean allowPriorityChannels) {
+ if (Flags.modesUi()) {
+ throw new IllegalStateException("can't be used with modesUI flag");
+ } else {
+ this.allowPriorityChannels = allowPriorityChannels;
+ }
+ }
+
+ public int getSuppressedVisualEffects() {
+ if (Flags.modesUi()) {
+ throw new IllegalStateException("can't be used with modesUI flag");
+ } else {
+ return this.suppressedVisualEffects;
+ }
+ }
+
+ public void setSuppressedVisualEffects(int suppressedVisualEffects) {
+ if (Flags.modesUi()) {
+ throw new IllegalStateException("can't be used with modesUI flag");
+ } else {
+ this.suppressedVisualEffects = suppressedVisualEffects;
+ }
+ }
+
+ public @ZenPolicy.ConversationSenders int getAllowConversationsFrom() {
+ if (Flags.modesUi()) {
+ return manualRule.zenPolicy.getPriorityConversationSenders();
+ }
+ return allowConversationsFrom;
+ }
+
+ public void setAllowConversationsFrom(
+ @ZenPolicy.ConversationSenders int allowConversationsFrom) {
+ if (Flags.modesUi()) {
+ throw new IllegalStateException("can't be used with modesUI flag");
+ } else {
+ this.allowConversationsFrom = allowConversationsFrom;
+ }
+ }
+
+ public void setAllowConversations(boolean allowConversations) {
+ if (Flags.modesUi()) {
+ throw new IllegalStateException("can't be used with modesUI flag");
+ } else {
+ this.allowConversations = allowConversations;
+ }
+ }
+
+ public boolean isAllowConversations() {
+ if (Flags.modesUi()) {
+ throw new IllegalStateException("can't be used with modesUI flag");
+ }
+ return allowConversations;
+ }
+
+ public @Policy.PrioritySenders int getAllowMessagesFrom() {
+ if (Flags.modesUi()) {
+ throw new IllegalStateException("can't be used with modesUI flag");
+ }
+ return allowMessagesFrom;
+ }
+
+ public void setAllowMessagesFrom(@Policy.PrioritySenders int allowMessagesFrom) {
+ if (Flags.modesUi()) {
+ throw new IllegalStateException("can't be used with modesUI flag");
+ } else {
+ this.allowMessagesFrom = allowMessagesFrom;
+ }
+ }
+
+ public void setAllowMessages(boolean allowMessages) {
+ if (Flags.modesUi()) {
+ throw new IllegalStateException("can't be used with modesUI flag");
+ }
+ this.allowMessages = allowMessages;
+ }
+
+ public @Policy.PrioritySenders int getAllowCallsFrom() {
+ if (Flags.modesUi()) {
+ return peopleTypeToPrioritySenders(
+ manualRule.zenPolicy.getPriorityCallSenders(), DEFAULT_CALLS_SOURCE);
+ }
+ return allowCallsFrom;
+ }
+
+ public void setAllowCallsFrom(@Policy.PrioritySenders int allowCallsFrom) {
+ if (Flags.modesUi()) {
+ manualRule.zenPolicy = new ZenPolicy.Builder(manualRule.zenPolicy)
+ .allowCalls(prioritySendersToPeopleType(allowCallsFrom))
+ .build();
+ } else {
+ this.allowCallsFrom = allowCallsFrom;
+ }
+ }
+
+ public void setAllowCalls(boolean allowCalls) {
+ if (Flags.modesUi()) {
+ throw new IllegalStateException("can't be used with modesUI flag");
+ }
+ this.allowCalls = allowCalls;
+ }
+
+ public boolean isAllowEvents() {
+ if (Flags.modesUi()) {
+ return manualRule.zenPolicy.isCategoryAllowed(
+ ZenPolicy.PRIORITY_CATEGORY_EVENTS, false);
+ }
+ return allowEvents;
+ }
+
+ public void setAllowEvents(boolean allowEvents) {
+ if (Flags.modesUi()) {
+ throw new IllegalStateException("can't be used with modesUI flag");
+ } else {
+ this.allowEvents = allowEvents;
+ }
+ }
+
+ public boolean isAllowReminders() {
+ if (Flags.modesUi()) {
+ throw new IllegalStateException("can't be used with modesUI flag");
+ }
+ return allowReminders;
+ }
+
+ public void setAllowReminders(boolean allowReminders) {
+ if (Flags.modesUi()) {
+ throw new IllegalStateException("can't be used with modesUI flag");
+ } else {
+ this.allowReminders = allowReminders;
+ }
+ }
+
+ public boolean isAllowMessages() {
+ if (Flags.modesUi()) {
+ throw new IllegalStateException("can't be used with modesUI flag");
+ }
+ return allowMessages;
+ }
+
+ public boolean isAllowRepeatCallers() {
+ if (Flags.modesUi()) {
+ throw new IllegalStateException("can't be used with modesUI flag");
+ }
+ return allowRepeatCallers;
+ }
+
+ public void setAllowRepeatCallers(boolean allowRepeatCallers) {
+ if (Flags.modesUi()) {
+ throw new IllegalStateException("can't be used with modesUI flag");
+ } else {
+ this.allowRepeatCallers = allowRepeatCallers;
+ }
+ }
+
+ public boolean isAllowSystem() {
+ if (Flags.modesUi()) {
+ throw new IllegalStateException("can't be used with modesUI flag");
+ }
+ return allowSystem;
+ }
+
+ public void setAllowSystem(boolean allowSystem) {
+ if (Flags.modesUi()) {
+ throw new IllegalStateException("can't be used with modesUI flag");
+ } else {
+ this.allowSystem = allowSystem;
+ }
+ }
+
+ public boolean isAllowMedia() {
+ if (Flags.modesUi()) {
+ throw new IllegalStateException("can't be used with modesUI flag");
+ }
+ return allowMedia;
+ }
+
+ public void setAllowMedia(boolean allowMedia) {
+ if (Flags.modesUi()) {
+ throw new IllegalStateException("can't be used with modesUI flag");
+ } else {
+ this.allowMedia = allowMedia;
+ }
+ }
+
+ public boolean isAllowAlarms() {
+ if (Flags.modesUi()) {
+ throw new IllegalStateException("can't be used with modesUI flag");
+ }
+ return allowAlarms;
+ }
+
+ public void setAllowAlarms(boolean allowAlarms) {
+ if (Flags.modesUi()) {
+ throw new IllegalStateException("can't be used with modesUI flag");
+ } else {
+ this.allowAlarms = allowAlarms;
+ }
+ }
+
+ public boolean isAllowCalls() {
+ if (Flags.modesUi()) {
+ throw new IllegalStateException("can't be used with modesUI flag");
+ }
+ return allowCalls;
+ }
+
private static String rulesToString(ArrayMap<String, ZenRule> ruleList) {
if (ruleList.isEmpty()) {
return "{}";
@@ -512,6 +828,8 @@
if (!(o instanceof ZenModeConfig)) return false;
if (o == this) return true;
final ZenModeConfig other = (ZenModeConfig) o;
+ // The policy fields that live on config are compared directly because the fields will
+ // contain data until MODES_UI is rolled out/cleaned up.
boolean eq = other.allowAlarms == allowAlarms
&& other.allowMedia == allowMedia
&& other.allowSystem == allowSystem
@@ -539,6 +857,8 @@
@Override
public int hashCode() {
+ // The policy fields that live on config are compared directly because the fields will
+ // contain data until MODES_UI is rolled out/cleaned up.
if (Flags.modesApi()) {
return Objects.hash(allowAlarms, allowMedia, allowSystem, allowCalls,
allowRepeatCallers, allowMessages,
@@ -619,9 +939,14 @@
rt.version = safeInt(parser, ZEN_ATT_VERSION, getCurrentXmlVersion());
rt.user = safeInt(parser, ZEN_ATT_USER, rt.user);
boolean readSuppressedEffects = false;
+ boolean readManualRule = false;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
tag = parser.getName();
if (type == XmlPullParser.END_TAG && ZEN_TAG.equals(tag)) {
+ if (Flags.modesUi() && !readManualRule) {
+ // migrate from fields on config into manual rule
+ rt.manualRule.zenPolicy = rt.toZenPolicy();
+ }
return rt;
}
if (type == XmlPullParser.START_TAG) {
@@ -693,6 +1018,9 @@
DEFAULT_SUPPRESSED_VISUAL_EFFECTS);
} else if (MANUAL_TAG.equals(tag)) {
rt.manualRule = readRuleXml(parser);
+ if (rt.manualRule != null) {
+ readManualRule = true;
+ }
} else if (AUTOMATIC_TAG.equals(tag)
|| (Flags.modesApi() && AUTOMATIC_DELETED_TAG.equals(tag))) {
final String id = parser.getAttributeValue(null, RULE_ATT_ID);
@@ -742,6 +1070,9 @@
? Integer.toString(xmlVersion) : Integer.toString(version));
out.attributeInt(null, ZEN_ATT_USER, user);
out.startTag(null, ALLOW_TAG);
+ // From MODES_UI these fields are only read if the flag has transitioned from off to on
+ // However, we will continue to write these fields until the flag is cleaned up so it's
+ // possible to turn the flag off without losing user data
out.attributeBoolean(null, ALLOW_ATT_CALLS, allowCalls);
out.attributeBoolean(null, ALLOW_ATT_REPEAT_CALLERS, allowRepeatCallers);
out.attributeBoolean(null, ALLOW_ATT_MESSAGES, allowMessages);
@@ -816,11 +1147,11 @@
rt.enabler = parser.getAttributeValue(null, RULE_ATT_ENABLER);
rt.condition = readConditionXml(parser);
- if (!Flags.modesApi() && rt.zenMode != Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+ if (!Flags.modesApi() && rt.zenMode != ZEN_MODE_IMPORTANT_INTERRUPTIONS
&& Condition.isValidId(rt.conditionId, SYSTEM_AUTHORITY)) {
// all default rules and user created rules updated to zenMode important interruptions
Slog.i(TAG, "Updating zenMode of automatic rule " + rt.name);
- rt.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ rt.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
}
rt.modified = safeBoolean(parser, RULE_ATT_MODIFIED, false);
rt.zenPolicy = readZenPolicyXml(parser);
@@ -952,7 +1283,7 @@
if (Flags.modesApi()) {
final int channels = safeInt(parser, ALLOW_ATT_CHANNELS, ZenPolicy.STATE_UNSET);
if (channels != ZenPolicy.STATE_UNSET) {
- builder.allowPriorityChannels(channels == ZenPolicy.STATE_ALLOW);
+ builder.allowPriorityChannels(channels == STATE_ALLOW);
policySet = true;
}
}
@@ -966,7 +1297,7 @@
policySet = true;
}
if (repeatCallers != ZenPolicy.STATE_UNSET) {
- builder.allowRepeatCallers(repeatCallers == ZenPolicy.STATE_ALLOW);
+ builder.allowRepeatCallers(repeatCallers == STATE_ALLOW);
policySet = true;
}
if (conversations != ZenPolicy.CONVERSATION_SENDERS_UNSET) {
@@ -974,23 +1305,23 @@
policySet = true;
}
if (alarms != ZenPolicy.STATE_UNSET) {
- builder.allowAlarms(alarms == ZenPolicy.STATE_ALLOW);
+ builder.allowAlarms(alarms == STATE_ALLOW);
policySet = true;
}
if (media != ZenPolicy.STATE_UNSET) {
- builder.allowMedia(media == ZenPolicy.STATE_ALLOW);
+ builder.allowMedia(media == STATE_ALLOW);
policySet = true;
}
if (system != ZenPolicy.STATE_UNSET) {
- builder.allowSystem(system == ZenPolicy.STATE_ALLOW);
+ builder.allowSystem(system == STATE_ALLOW);
policySet = true;
}
if (events != ZenPolicy.STATE_UNSET) {
- builder.allowEvents(events == ZenPolicy.STATE_ALLOW);
+ builder.allowEvents(events == STATE_ALLOW);
policySet = true;
}
if (reminders != ZenPolicy.STATE_UNSET) {
- builder.allowReminders(reminders == ZenPolicy.STATE_ALLOW);
+ builder.allowReminders(reminders == STATE_ALLOW);
policySet = true;
}
@@ -1005,31 +1336,31 @@
ZenPolicy.STATE_UNSET);
if (fullScreenIntent != ZenPolicy.STATE_UNSET) {
- builder.showFullScreenIntent(fullScreenIntent == ZenPolicy.STATE_ALLOW);
+ builder.showFullScreenIntent(fullScreenIntent == STATE_ALLOW);
policySet = true;
}
if (lights != ZenPolicy.STATE_UNSET) {
- builder.showLights(lights == ZenPolicy.STATE_ALLOW);
+ builder.showLights(lights == STATE_ALLOW);
policySet = true;
}
if (peek != ZenPolicy.STATE_UNSET) {
- builder.showPeeking(peek == ZenPolicy.STATE_ALLOW);
+ builder.showPeeking(peek == STATE_ALLOW);
policySet = true;
}
if (statusBar != ZenPolicy.STATE_UNSET) {
- builder.showStatusBarIcons(statusBar == ZenPolicy.STATE_ALLOW);
+ builder.showStatusBarIcons(statusBar == STATE_ALLOW);
policySet = true;
}
if (badges != ZenPolicy.STATE_UNSET) {
- builder.showBadges(badges == ZenPolicy.STATE_ALLOW);
+ builder.showBadges(badges == STATE_ALLOW);
policySet = true;
}
if (ambient != ZenPolicy.STATE_UNSET) {
- builder.showInAmbientDisplay(ambient == ZenPolicy.STATE_ALLOW);
+ builder.showInAmbientDisplay(ambient == STATE_ALLOW);
policySet = true;
}
if (notificationList != ZenPolicy.STATE_UNSET) {
- builder.showInNotificationList(notificationList == ZenPolicy.STATE_ALLOW);
+ builder.showInNotificationList(notificationList == STATE_ALLOW);
policySet = true;
}
@@ -1266,17 +1597,22 @@
}
};
+ public ZenPolicy getZenPolicy() {
+ return Flags.modesUi() ? manualRule.zenPolicy : toZenPolicy();
+ }
+
/**
* Converts a ZenModeConfig to a ZenPolicy
*/
- public ZenPolicy toZenPolicy() {
+ @VisibleForTesting
+ ZenPolicy toZenPolicy() {
ZenPolicy.Builder builder = new ZenPolicy.Builder()
.allowCalls(allowCalls
- ? notificationPolicySendersToZenPolicyPeopleType(allowCallsFrom)
+ ? prioritySendersToPeopleType(allowCallsFrom)
: ZenPolicy.PEOPLE_TYPE_NONE)
.allowRepeatCallers(allowRepeatCallers)
.allowMessages(allowMessages
- ? notificationPolicySendersToZenPolicyPeopleType(allowMessagesFrom)
+ ? prioritySendersToPeopleType(allowMessagesFrom)
: ZenPolicy.PEOPLE_TYPE_NONE)
.allowReminders(allowReminders)
.allowEvents(allowEvents)
@@ -1336,7 +1672,7 @@
if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_MESSAGES,
isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_MESSAGES, defaultPolicy))) {
priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES;
- messageSenders = zenPolicyPeopleTypeToNotificationPolicySenders(
+ messageSenders = peopleTypeToPrioritySenders(
zenPolicy.getPriorityMessageSenders(), messageSenders);
}
@@ -1352,7 +1688,7 @@
if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_CALLS,
isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_CALLS, defaultPolicy))) {
priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS;
- callSenders = zenPolicyPeopleTypeToNotificationPolicySenders(
+ callSenders = peopleTypeToPrioritySenders(
zenPolicy.getPriorityCallSenders(), callSenders);
}
@@ -1452,46 +1788,155 @@
return (policy.suppressedVisualEffects & visualEffect) == 0;
}
+ private boolean isVisualEffectAllowed(int suppressedVisualEffects, int visualEffect) {
+ return (suppressedVisualEffects & visualEffect) == 0;
+ }
+
public Policy toNotificationPolicy() {
int priorityCategories = 0;
int priorityCallSenders = Policy.PRIORITY_SENDERS_CONTACTS;
int priorityMessageSenders = Policy.PRIORITY_SENDERS_CONTACTS;
int priorityConversationSenders = Policy.CONVERSATION_SENDERS_IMPORTANT;
- if (allowConversations) {
- priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS;
- }
- if (allowCalls) {
- priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS;
- }
- if (allowMessages) {
- priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES;
- }
- if (allowEvents) {
- priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS;
- }
- if (allowReminders) {
- priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS;
- }
- if (allowRepeatCallers) {
- priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS;
- }
- if (allowAlarms) {
- priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS;
- }
- if (allowMedia) {
- priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA;
- }
- if (allowSystem) {
- priorityCategories |= Policy.PRIORITY_CATEGORY_SYSTEM;
- }
- priorityCallSenders = sourceToPrioritySenders(allowCallsFrom, priorityCallSenders);
- priorityMessageSenders = sourceToPrioritySenders(allowMessagesFrom, priorityMessageSenders);
- priorityConversationSenders = zenPolicyConversationSendersToNotificationPolicy(
- allowConversationsFrom, priorityConversationSenders);
+ int state = 0;
+ int suppressedVisualEffects = 0;
- int state = areChannelsBypassingDnd ? Policy.STATE_CHANNELS_BYPASSING_DND : 0;
- if (Flags.modesApi()) {
- state = Policy.policyState(areChannelsBypassingDnd, allowPriorityChannels);
+ if (Flags.modesUi()) {
+ if (manualRule.zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_EVENTS, false)) {
+ priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS;
+ }
+ if (manualRule.zenPolicy.isCategoryAllowed(
+ ZenPolicy.PRIORITY_CATEGORY_REMINDERS, false)) {
+ priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS;
+ }
+ if (manualRule.zenPolicy.isCategoryAllowed(
+ ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS, false)) {
+ priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS;
+ }
+ if (manualRule.zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_ALARMS, false)) {
+ priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS;
+ }
+ if (manualRule.zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_MEDIA, false)) {
+ priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA;
+ }
+ if (manualRule.zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_SYSTEM, false)) {
+ priorityCategories |= Policy.PRIORITY_CATEGORY_SYSTEM;
+ }
+
+ if (manualRule.zenPolicy.getPriorityCategoryConversations() == STATE_ALLOW) {
+ priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS;
+ }
+ priorityConversationSenders = zenPolicyConversationSendersToNotificationPolicy(
+ manualRule.zenPolicy.getPriorityConversationSenders(),
+ CONVERSATION_SENDERS_NONE);
+ if (manualRule.zenPolicy.getPriorityCategoryCalls() == STATE_ALLOW) {
+ priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS;
+ }
+ priorityCallSenders = peopleTypeToPrioritySenders(
+ manualRule.zenPolicy.getPriorityCallSenders(), DEFAULT_CALLS_SOURCE);
+ if (manualRule.zenPolicy.getPriorityCategoryMessages() == STATE_ALLOW) {
+ priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES;
+ }
+ priorityMessageSenders = peopleTypeToPrioritySenders(
+ manualRule.zenPolicy.getPriorityMessageSenders(), DEFAULT_SOURCE);
+
+ state = Policy.policyState(areChannelsBypassingDnd,
+ manualRule.zenPolicy.getPriorityChannelsAllowed() != STATE_DISALLOW);
+
+ boolean suppressFullScreenIntent = !manualRule.zenPolicy.isVisualEffectAllowed(
+ ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT,
+ isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS,
+ ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT));
+
+ boolean suppressLights = !manualRule.zenPolicy.isVisualEffectAllowed(
+ ZenPolicy.VISUAL_EFFECT_LIGHTS,
+ isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS,
+ ZenPolicy.VISUAL_EFFECT_LIGHTS));
+
+ boolean suppressAmbient = !manualRule.zenPolicy.isVisualEffectAllowed(
+ ZenPolicy.VISUAL_EFFECT_AMBIENT,
+ isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS,
+ ZenPolicy.VISUAL_EFFECT_AMBIENT));
+
+ if (suppressFullScreenIntent && suppressLights && suppressAmbient) {
+ suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
+ }
+
+ if (suppressFullScreenIntent) {
+ suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
+ }
+
+ if (suppressLights) {
+ suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS;
+ }
+
+ if (!manualRule.zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_PEEK,
+ isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS,
+ ZenPolicy.VISUAL_EFFECT_PEEK))) {
+ suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_PEEK;
+ suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_ON;
+ }
+
+ if (!manualRule.zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_STATUS_BAR,
+ isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS,
+ ZenPolicy.VISUAL_EFFECT_STATUS_BAR))) {
+ suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_STATUS_BAR;
+ }
+
+ if (!manualRule.zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_BADGE,
+ isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS,
+ ZenPolicy.VISUAL_EFFECT_BADGE))) {
+ suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE;
+ }
+
+ if (suppressAmbient) {
+ suppressedVisualEffects |= SUPPRESSED_EFFECT_AMBIENT;
+ }
+
+ if (!manualRule.zenPolicy.isVisualEffectAllowed(
+ ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST,
+ isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS,
+ ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST))) {
+ suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
+ }
+ } else {
+ if (isAllowConversations()) {
+ priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS;
+ }
+ if (isAllowCalls()) {
+ priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS;
+ }
+ if (isAllowMessages()) {
+ priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES;
+ }
+ if (isAllowEvents()) {
+ priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS;
+ }
+ if (isAllowReminders()) {
+ priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS;
+ }
+ if (isAllowRepeatCallers()) {
+ priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS;
+ }
+ if (isAllowAlarms()) {
+ priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS;
+ }
+ if (isAllowMedia()) {
+ priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA;
+ }
+ if (isAllowSystem()) {
+ priorityCategories |= Policy.PRIORITY_CATEGORY_SYSTEM;
+ }
+ priorityCallSenders = sourceToPrioritySenders(getAllowCallsFrom(), priorityCallSenders);
+ priorityMessageSenders = sourceToPrioritySenders(
+ getAllowMessagesFrom(), priorityMessageSenders);
+ priorityConversationSenders = zenPolicyConversationSendersToNotificationPolicy(
+ getAllowConversationsFrom(), priorityConversationSenders);
+
+ state = areChannelsBypassingDnd ? Policy.STATE_CHANNELS_BYPASSING_DND : 0;
+ if (Flags.modesApi()) {
+ state = Policy.policyState(areChannelsBypassingDnd, allowPriorityChannels);
+ }
+ suppressedVisualEffects = getSuppressedVisualEffects();
}
return new Policy(priorityCategories, priorityCallSenders, priorityMessageSenders,
@@ -1544,31 +1989,38 @@
public void applyNotificationPolicy(Policy policy) {
if (policy == null) return;
- allowAlarms = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_ALARMS) != 0;
- allowMedia = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MEDIA) != 0;
- allowSystem = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_SYSTEM) != 0;
- allowEvents = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_EVENTS) != 0;
- allowReminders = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REMINDERS) != 0;
- allowCalls = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_CALLS) != 0;
- allowMessages = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MESSAGES) != 0;
- allowRepeatCallers = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REPEAT_CALLERS)
- != 0;
- allowCallsFrom = normalizePrioritySenders(policy.priorityCallSenders, allowCallsFrom);
- allowMessagesFrom = normalizePrioritySenders(policy.priorityMessageSenders,
- allowMessagesFrom);
- if (policy.suppressedVisualEffects != Policy.SUPPRESSED_EFFECTS_UNSET) {
- suppressedVisualEffects = policy.suppressedVisualEffects;
+ if (Flags.modesUi()) {
+ manualRule.zenPolicy = ZenAdapters.notificationPolicyToZenPolicy(policy);
+ } else {
+ setAllowAlarms((policy.priorityCategories & Policy.PRIORITY_CATEGORY_ALARMS) != 0);
+ allowMedia = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MEDIA) != 0;
+ allowSystem = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_SYSTEM) != 0;
+ allowEvents = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_EVENTS) != 0;
+ allowReminders = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REMINDERS) != 0;
+ allowCalls = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_CALLS) != 0;
+ allowMessages = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MESSAGES) != 0;
+ allowRepeatCallers =
+ (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REPEAT_CALLERS)
+ != 0;
+ allowCallsFrom = normalizePrioritySenders(policy.priorityCallSenders, allowCallsFrom);
+ allowMessagesFrom = normalizePrioritySenders(policy.priorityMessageSenders,
+ allowMessagesFrom);
+ if (policy.suppressedVisualEffects != Policy.SUPPRESSED_EFFECTS_UNSET) {
+ suppressedVisualEffects = policy.suppressedVisualEffects;
+ }
+ allowConversations = (policy.priorityCategories
+ & Policy.PRIORITY_CATEGORY_CONVERSATIONS) != 0;
+ allowConversationsFrom = normalizeConversationSenders(allowConversations,
+ policy.priorityConversationSenders,
+ allowConversationsFrom);
+ if (policy.state != Policy.STATE_UNSET) {
+ if (Flags.modesApi()) {
+ setAllowPriorityChannels(policy.allowPriorityChannels());
+ }
+ }
}
- allowConversations = (policy.priorityCategories
- & Policy.PRIORITY_CATEGORY_CONVERSATIONS) != 0;
- allowConversationsFrom = normalizeConversationSenders(allowConversations,
- policy.priorityConversationSenders,
- allowConversationsFrom);
if (policy.state != Policy.STATE_UNSET) {
areChannelsBypassingDnd = (policy.state & Policy.STATE_CHANNELS_BYPASSING_DND) != 0;
- if (Flags.modesApi()) {
- allowPriorityChannels = policy.allowPriorityChannels();
- }
}
}
@@ -1995,49 +2447,11 @@
return "";
}
- public static String getConditionSummary(Context context, ZenModeConfig config,
- int userHandle, boolean shortVersion) {
- return getConditionLine(context, config, userHandle, false /*useLine1*/, shortVersion);
- }
-
- private static String getConditionLine(Context context, ZenModeConfig config,
- int userHandle, boolean useLine1, boolean shortVersion) {
- if (config == null) return "";
- String summary = "";
- if (config.manualRule != null) {
- final Uri id = config.manualRule.conditionId;
- if (config.manualRule.enabler != null) {
- summary = getOwnerCaption(context, config.manualRule.enabler);
- } else {
- if (id == null) {
- summary = context.getString(com.android.internal.R.string.zen_mode_forever);
- } else {
- final long time = tryParseCountdownConditionId(id);
- Condition c = config.manualRule.condition;
- if (time > 0) {
- final long now = System.currentTimeMillis();
- final long span = time - now;
- c = toTimeCondition(context, time, Math.round(span / (float) MINUTES_MS),
- userHandle, shortVersion);
- }
- final String rt = c == null ? "" : useLine1 ? c.line1 : c.summary;
- summary = TextUtils.isEmpty(rt) ? "" : rt;
- }
- }
+ public boolean isManualActive() {
+ if (!Flags.modesUi()) {
+ return manualRule != null;
}
- for (ZenRule automaticRule : config.automaticRules.values()) {
- if (automaticRule.isAutomaticActive()) {
- if (summary.isEmpty()) {
- summary = automaticRule.name;
- } else {
- summary = context.getResources()
- .getString(R.string.zen_mode_rule_name_combination, summary,
- automaticRule.name);
- }
-
- }
- }
- return summary;
+ return manualRule != null && manualRule.isAutomaticActive();
}
public static class ZenRule implements Parcelable {
@@ -2401,7 +2815,7 @@
public static boolean isZenOverridingRinger(int zen, Policy consolidatedPolicy) {
return zen == Global.ZEN_MODE_NO_INTERRUPTIONS
|| zen == Global.ZEN_MODE_ALARMS
- || (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+ || (zen == ZEN_MODE_IMPORTANT_INTERRUPTIONS
&& ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(consolidatedPolicy));
}
@@ -2410,21 +2824,40 @@
* This includes notification, ringer and system sounds
*/
public static boolean areAllPriorityOnlyRingerSoundsMuted(ZenModeConfig config) {
- boolean areChannelsBypassingDnd = config.areChannelsBypassingDnd;
- if (Flags.modesApi()) {
- areChannelsBypassingDnd = config.areChannelsBypassingDnd
- && config.allowPriorityChannels;
+ if (Flags.modesUi()) {
+ final ZenPolicy policy = config.manualRule.zenPolicy;
+ return !policy.isCategoryAllowed(PRIORITY_CATEGORY_REMINDERS, false)
+ && !policy.isCategoryAllowed(PRIORITY_CATEGORY_CALLS, false)
+ && !policy.isCategoryAllowed(PRIORITY_CATEGORY_MESSAGES, false)
+ && !policy.isCategoryAllowed(PRIORITY_CATEGORY_EVENTS, false)
+ && !policy.isCategoryAllowed(PRIORITY_CATEGORY_REPEAT_CALLERS, false)
+ && !policy.isCategoryAllowed(PRIORITY_CATEGORY_SYSTEM, false)
+ && !(config.areChannelsBypassingDnd && policy.getPriorityChannelsAllowed()
+ == STATE_ALLOW);
+
+ } else {
+ boolean areChannelsBypassingDnd = config.areChannelsBypassingDnd;
+ if (Flags.modesApi()) {
+ areChannelsBypassingDnd = config.areChannelsBypassingDnd
+ && config.isAllowPriorityChannels();
+ }
+ return !config.isAllowReminders() && !config.isAllowCalls() && !config.isAllowMessages()
+ && !config.isAllowEvents() && !config.isAllowRepeatCallers()
+ && !areChannelsBypassingDnd && !config.isAllowSystem();
}
- return !config.allowReminders && !config.allowCalls && !config.allowMessages
- && !config.allowEvents && !config.allowRepeatCallers
- && !areChannelsBypassingDnd && !config.allowSystem;
}
/**
* Determines whether dnd mutes all sounds
*/
public static boolean areAllZenBehaviorSoundsMuted(ZenModeConfig config) {
- return !config.allowAlarms && !config.allowMedia
+ if (Flags.modesUi()) {
+ final ZenPolicy policy = config.manualRule.zenPolicy;
+ return !policy.isCategoryAllowed(PRIORITY_CATEGORY_ALARMS, false)
+ && !policy.isCategoryAllowed(PRIORITY_CATEGORY_MEDIA, false)
+ && areAllPriorityOnlyRingerSoundsMuted(config);
+ }
+ return !config.isAllowAlarms() && !config.isAllowMedia()
&& areAllPriorityOnlyRingerSoundsMuted(config);
}
diff --git a/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java b/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
index a77e076..f123a96 100644
--- a/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
+++ b/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
@@ -101,6 +101,11 @@
private static final String TAG = OnDeviceSandboxedInferenceService.class.getSimpleName();
/**
+ * @hide
+ */
+ public static final String INFERENCE_INFO_BUNDLE_KEY = "inference_info";
+
+ /**
* The {@link Intent} that must be declared as handled by the service. To be supported, the
* service must also require the
* {@link android.Manifest.permission#BIND_ON_DEVICE_SANDBOXED_INFERENCE_SERVICE}
diff --git a/core/java/android/service/wearable/WearableSensingService.java b/core/java/android/service/wearable/WearableSensingService.java
index ac22e70..3735c43 100644
--- a/core/java/android/service/wearable/WearableSensingService.java
+++ b/core/java/android/service/wearable/WearableSensingService.java
@@ -398,8 +398,8 @@
/**
* Called when a data request observer is registered. Each request must not be larger than
* {@link WearableSensingDataRequest#getMaxRequestSize()}. In addition, at most {@link
- * WearableSensingDataRequester#getRateLimit()} requests can be sent every rolling {@link
- * WearableSensingDataRequester#getRateLimitWindowSize()}. Requests that are too large or too
+ * WearableSensingDataRequest#getRateLimit()} requests can be sent every rolling {@link
+ * WearableSensingDataRequest#getRateLimitWindowSize()}. Requests that are too large or too
* frequent will be dropped by the system. See {@link
* WearableSensingDataRequester#requestData(WearableSensingDataRequest, Consumer)} for details
* about the status code returned for each request.
@@ -442,7 +442,7 @@
* @param packageName The package name of the app that will receive the requests sent to the
* dataRequester.
* @param dataRequester A handle to the observer to be unregistered. It is the exact same
- * instance provided in a previous {@link #onDataRequestConsumerRegistered(int, String,
+ * instance provided in a previous {@link #onDataRequestObserverRegistered(int, String,
* WearableSensingDataRequester, Consumer)} invocation.
* @param statusConsumer the consumer for the status of the data request observer
* unregistration. This is different from the status for each data request.
@@ -469,7 +469,7 @@
* in which case it should return the corresponding status code.
*
* <p>The implementation should also store the {@code statusConsumer}. If the wearable stops
- * listening for hotword for any reason other than {@link #onStopListeningForHotword(Consumer)}
+ * listening for hotword for any reason other than {@link #onStopHotwordRecognition(Consumer)}
* being invoked, it should send an appropriate status code listed in {@link
* WearableSensingManager} to {@code statusConsumer}. If the error condition cannot be described
* by any of those status codes, it should send a {@link WearableSensingManager#STATUS_UNKNOWN}.
@@ -514,11 +514,11 @@
/**
* Called when hotword audio data sent to the {@code hotwordAudioConsumer} in {@link
- * #onStartListeningForHotword(Consumer, Consumer)} is accepted by the
+ * #onStartHotwordRecognition(Consumer, Consumer)} is accepted by the
* {@link android.service.voice.HotwordDetectionService} as valid hotword.
*
* <p>After the implementation of this class sends the hotword audio data to the {@code
- * hotwordAudioConsumer} in {@link #onStartListeningForHotword(Consumer,
+ * hotwordAudioConsumer} in {@link #onStartHotwordRecognition(Consumer,
* Consumer)}, the system will forward the data into {@link
* android.service.voice.HotwordDetectionService} (which runs in an isolated process) for
* second-stage hotword detection. If accepted as valid hotword there, this method will be
@@ -545,7 +545,7 @@
*
* <p>This method is expected to be overridden by a derived class. The implementation should
* stop sending hotword audio data to the {@code hotwordAudioConsumer} in {@link
- * #onStartListeningForHotword(Consumer, Consumer)}
+ * #onStartHotwordRecognition(Consumer, Consumer)}
*/
@FlaggedApi(Flags.FLAG_ENABLE_HOTWORD_WEARABLE_SENSING_API)
@BinderThread
diff --git a/core/java/android/telephony/DropBoxManagerLoggerBackend.java b/core/java/android/telephony/DropBoxManagerLoggerBackend.java
new file mode 100644
index 0000000..25a3b9f
--- /dev/null
+++ b/core/java/android/telephony/DropBoxManagerLoggerBackend.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.DropBoxManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.util.Log;
+
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.Optional;
+
+/**
+ * A persistent logger backend that stores logs in Android DropBoxManager
+ *
+ * @hide
+ */
+public class DropBoxManagerLoggerBackend implements PersistentLoggerBackend {
+
+ private static final String TAG = "DropBoxManagerLoggerBackend";
+ // Separate tag reference to be explicitly used for dropboxmanager instead of logcat logging
+ private static final String DROPBOX_TAG = "DropBoxManagerLoggerBackend";
+ private static final DateTimeFormatter LOG_TIMESTAMP_FORMATTER =
+ DateTimeFormatter.ofPattern("MM-dd HH:mm:ss.SSS");
+ private static final ZoneId LOCAL_ZONE_ID = ZoneId.systemDefault();
+ private static final int BUFFER_SIZE_BYTES = 500 * 1024; // 500 KB
+ private static final int MIN_BUFFER_BYTES_FOR_FLUSH = 5 * 1024; // 5 KB
+
+ private static DropBoxManagerLoggerBackend sInstance;
+
+ private final DropBoxManager mDropBoxManager;
+ private final Object mBufferLock = new Object();
+ @GuardedBy("mBufferLock")
+ private final StringBuilder mLogBuffer = new StringBuilder();
+ private long mBufferStartTime = -1L;
+ private final HandlerThread mHandlerThread = new HandlerThread(DROPBOX_TAG);
+ private final Handler mHandler;
+ // Flag for determining if logging is enabled as a general feature
+ private final boolean mDropBoxManagerLoggingEnabled;
+ // Flag for controlling if logging is enabled at runtime
+ private boolean mIsLoggingEnabled = false;
+
+ /**
+ * Returns a singleton instance of {@code DropBoxManagerLoggerBackend} that will log to
+ * DropBoxManager if the config_dropboxmanager_persistent_logging_enabled resource config is
+ * enabled.
+ * @param context Android context
+ */
+ @Nullable
+ public static synchronized DropBoxManagerLoggerBackend getInstance(@NonNull Context context) {
+ if (sInstance == null) {
+ sInstance = new DropBoxManagerLoggerBackend(context);
+ }
+ return sInstance;
+ }
+
+ private DropBoxManagerLoggerBackend(@NonNull Context context) {
+ mDropBoxManager = context.getSystemService(DropBoxManager.class);
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+ mDropBoxManagerLoggingEnabled = persistentLoggingEnabled(context);
+ }
+
+ private boolean persistentLoggingEnabled(@NonNull Context context) {
+ try {
+ return context.getResources().getBoolean(
+ R.bool.config_dropboxmanager_persistent_logging_enabled);
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Persistent logging config not found");
+ return false;
+ }
+ }
+
+ /**
+ * Enable or disable logging to DropBoxManager
+ * @param isLoggingEnabled Whether logging should be enabled
+ */
+ public void setLoggingEnabled(boolean isLoggingEnabled) {
+ Log.i(DROPBOX_TAG, "toggle logging: " + isLoggingEnabled);
+ mIsLoggingEnabled = isLoggingEnabled;
+ }
+
+ /**
+ * Persist a DEBUG log message.
+ * @param tag Used to identify the source of a log message.
+ * @param msg The message you would like logged.
+ */
+ public void debug(@NonNull String tag, @NonNull String msg) {
+ if (!mDropBoxManagerLoggingEnabled) {
+ return;
+ }
+ bufferLog("D", tag, msg, Optional.empty());
+ }
+
+ /**
+ * Persist a INFO log message.
+ * @param tag Used to identify the source of a log message.
+ * @param msg The message you would like logged.
+ */
+ public void info(@NonNull String tag, @NonNull String msg) {
+ if (!mDropBoxManagerLoggingEnabled) {
+ return;
+ }
+ bufferLog("I", tag, msg, Optional.empty());
+ }
+
+ /**
+ * Persist a WARN log message.
+ * @param tag Used to identify the source of a log message.
+ * @param msg The message you would like logged.
+ */
+ public void warn(@NonNull String tag, @NonNull String msg) {
+ if (!mDropBoxManagerLoggingEnabled) {
+ return;
+ }
+ bufferLog("W", tag, msg, Optional.empty());
+ }
+
+ /**
+ * Persist a WARN log message.
+ * @param tag Used to identify the source of a log message.
+ * @param msg The message you would like logged.
+ * @param t An exception to log.
+ */
+ public void warn(@NonNull String tag, @NonNull String msg, @NonNull Throwable t) {
+ if (!mDropBoxManagerLoggingEnabled) {
+ return;
+ }
+ bufferLog("W", tag, msg, Optional.of(t));
+ }
+
+ /**
+ * Persist a ERROR log message.
+ * @param tag Used to identify the source of a log message.
+ * @param msg The message you would like logged.
+ */
+ public void error(@NonNull String tag, @NonNull String msg) {
+ if (!mDropBoxManagerLoggingEnabled) {
+ return;
+ }
+ bufferLog("E", tag, msg, Optional.empty());
+ }
+
+ /**
+ * Persist a ERROR log message.
+ * @param tag Used to identify the source of a log message.
+ * @param msg The message you would like logged.
+ * @param t An exception to log.
+ */
+ public void error(@NonNull String tag, @NonNull String msg, @NonNull Throwable t) {
+ if (!mDropBoxManagerLoggingEnabled) {
+ return;
+ }
+ bufferLog("E", tag, msg, Optional.of(t));
+ }
+
+ private synchronized void bufferLog(
+ @NonNull String level,
+ @NonNull String tag,
+ @NonNull String msg,
+ Optional<Throwable> t) {
+ if (!mIsLoggingEnabled) {
+ return;
+ }
+
+ if (mBufferStartTime == -1L) {
+ mBufferStartTime = System.currentTimeMillis();
+ }
+
+ synchronized (mBufferLock) {
+ mLogBuffer
+ .append(formatLog(level, tag, msg, t))
+ .append("\n");
+
+ if (mLogBuffer.length() >= BUFFER_SIZE_BYTES) {
+ flushAsync();
+ }
+ }
+ }
+
+ private String formatLog(
+ @NonNull String level,
+ @NonNull String tag,
+ @NonNull String msg,
+ Optional<Throwable> t) {
+ // Expected format = "$Timestamp $Level $Tag: $Message"
+ return formatTimestamp(System.currentTimeMillis()) + " " + level + " " + tag + ": "
+ + t.map(throwable -> msg + ": " + Log.getStackTraceString(throwable)).orElse(msg);
+ }
+
+ private String formatTimestamp(long currentTimeMillis) {
+ return Instant.ofEpochMilli(currentTimeMillis)
+ .atZone(LOCAL_ZONE_ID)
+ .format(LOG_TIMESTAMP_FORMATTER);
+ }
+
+ /**
+ * Flushes all buffered logs into DropBoxManager as a single log record with a tag of
+ * {@link #DROPBOX_TAG} asynchronously. Should be invoked sparingly as DropBoxManager has
+ * device-level limitations on the number files that can be stored.
+ */
+ public void flushAsync() {
+ if (!mDropBoxManagerLoggingEnabled) {
+ return;
+ }
+
+ mHandler.post(this::flush);
+ };
+
+ /**
+ * Flushes all buffered logs into DropBoxManager as a single log record with a tag of
+ * {@link #DROPBOX_TAG}. Should be invoked sparingly as DropBoxManager has device-level
+ * limitations on the number files that can be stored.
+ */
+ public void flush() {
+ if (!mDropBoxManagerLoggingEnabled) {
+ return;
+ }
+
+ synchronized (mBufferLock) {
+ if (mLogBuffer.length() < MIN_BUFFER_BYTES_FOR_FLUSH) {
+ return;
+ }
+
+ Log.d(DROPBOX_TAG, "Flushing logs from "
+ + formatTimestamp(mBufferStartTime) + " to "
+ + formatTimestamp(System.currentTimeMillis()));
+
+ try {
+ mDropBoxManager.addText(DROPBOX_TAG, mLogBuffer.toString());
+ } catch (Exception e) {
+ Log.w(DROPBOX_TAG, "Failed to flush logs of length "
+ + mLogBuffer.length() + " to DropBoxManager", e);
+ }
+ mLogBuffer.setLength(0);
+ }
+ mBufferStartTime = -1L;
+ }
+}
diff --git a/core/java/android/telephony/PersistentLogger.java b/core/java/android/telephony/PersistentLogger.java
new file mode 100644
index 0000000..8b12a1c
--- /dev/null
+++ b/core/java/android/telephony/PersistentLogger.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.NonNull;
+
+/**
+ * A persistent logging client. Intended for persisting critical debug logs in situations where
+ * standard Android logcat logs may not be retained long enough.
+ *
+ * @hide
+ */
+public class PersistentLogger {
+ private final PersistentLoggerBackend mPersistentLoggerBackend;
+
+ public PersistentLogger(@NonNull PersistentLoggerBackend persistentLoggerBackend) {
+ mPersistentLoggerBackend = persistentLoggerBackend;
+ }
+
+ /**
+ * Persist a DEBUG log message.
+ * @param tag Used to identify the source of a log message.
+ * @param msg The message you would like logged.
+ */
+ public void debug(@NonNull String tag, @NonNull String msg) {
+ mPersistentLoggerBackend.debug(tag, msg);
+ }
+
+ /**
+ * Persist a INFO log message.
+ * @param tag Used to identify the source of a log message.
+ * @param msg The message you would like logged.
+ */
+ public void info(@NonNull String tag, @NonNull String msg) {
+ mPersistentLoggerBackend.info(tag, msg);
+ }
+
+ /**
+ * Persist a WARN log message.
+ * @param tag Used to identify the source of a log message.
+ * @param msg The message you would like logged.
+ */
+ public void warn(@NonNull String tag, @NonNull String msg) {
+ mPersistentLoggerBackend.warn(tag, msg);
+ }
+
+ /**
+ * Persist a WARN log message.
+ * @param tag Used to identify the source of a log message.
+ * @param msg The message you would like logged.
+ * @param t An exception to log.
+ */
+ public void warn(@NonNull String tag, @NonNull String msg, @NonNull Throwable t) {
+ mPersistentLoggerBackend.warn(tag, msg, t);
+ }
+
+ /**
+ * Persist a ERROR log message.
+ * @param tag Used to identify the source of a log message.
+ * @param msg The message you would like logged.
+ */
+ public void error(@NonNull String tag, @NonNull String msg) {
+ mPersistentLoggerBackend.error(tag, msg);
+ }
+
+ /**
+ * Persist a ERROR log message.
+ * @param tag Used to identify the source of a log message.
+ * @param msg The message you would like logged.
+ * @param t An exception to log.
+ */
+ public void error(@NonNull String tag, @NonNull String msg, @NonNull Throwable t) {
+ mPersistentLoggerBackend.error(tag, msg, t);
+ }
+}
diff --git a/core/java/android/telephony/PersistentLoggerBackend.java b/core/java/android/telephony/PersistentLoggerBackend.java
new file mode 100644
index 0000000..e3e72e1
--- /dev/null
+++ b/core/java/android/telephony/PersistentLoggerBackend.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.annotation.NonNull;
+
+/**
+ * Interface for logging backends to provide persistent log storage.
+ *
+ * @hide
+ */
+public interface PersistentLoggerBackend {
+
+ /**
+ * Persist a DEBUG log message.
+ * @param tag Used to identify the source of a log message.
+ * @param msg The message you would like logged.
+ */
+ void debug(@NonNull String tag, @NonNull String msg);
+
+ /**
+ * Persist a INFO log message.
+ * @param tag Used to identify the source of a log message.
+ * @param msg The message you would like logged.
+ */
+ void info(@NonNull String tag, @NonNull String msg);
+
+ /**
+ * Persist a WARN log message.
+ * @param tag Used to identify the source of a log message.
+ * @param msg The message you would like logged.
+ */
+ void warn(@NonNull String tag, @NonNull String msg);
+
+ /**
+ * Persist a WARN log message.
+ * @param tag Used to identify the source of a log message.
+ * @param msg The message you would like logged.
+ * @param t An exception to log.
+ */
+ void warn(@NonNull String tag, @NonNull String msg, @NonNull Throwable t);
+
+ /**
+ * Persist a ERROR log message.
+ * @param tag Used to identify the source of a log message.
+ * @param msg The message you would like logged.
+ */
+ void error(@NonNull String tag, @NonNull String msg);
+
+ /**
+ * Persist a ERROR log message.
+ * @param tag Used to identify the source of a log message.
+ * @param msg The message you would like logged.
+ * @param t An exception to log.
+ */
+ void error(@NonNull String tag, @NonNull String msg, @NonNull Throwable t);
+}
diff --git a/core/java/android/telephony/SubscriptionPlan.aidl b/core/java/android/telephony/SubscriptionPlan.aidl
old mode 100755
new mode 100644
diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java
old mode 100755
new mode 100644
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
old mode 100755
new mode 100644
diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java
index 57d1b8d..ccec89b 100644
--- a/core/java/android/view/HandwritingInitiator.java
+++ b/core/java/android/view/HandwritingInitiator.java
@@ -496,9 +496,10 @@
if (delegatorPackageName == null) {
delegatorPackageName = view.getContext().getOpPackageName();
}
+ WeakReference<View> viewRef = new WeakReference<>(view);
Consumer<Boolean> consumer = delegationAccepted -> {
if (delegationAccepted) {
- onDelegationAccepted(view);
+ onDelegationAccepted(viewRef.get());
}
};
mImm.acceptStylusHandwritingDelegation(view, delegatorPackageName, view::post, consumer);
@@ -509,6 +510,10 @@
mState.mHandled = true;
mState.mShouldInitHandwriting = false;
}
+ if (view == null) {
+ // can be null if view was detached and was GCed.
+ return;
+ }
if (view instanceof TextView) {
((TextView) view).hideHint();
}
diff --git a/core/java/android/view/HdrRenderState.java b/core/java/android/view/HdrRenderState.java
index eadc507..c6b3937 100644
--- a/core/java/android/view/HdrRenderState.java
+++ b/core/java/android/view/HdrRenderState.java
@@ -65,6 +65,7 @@
void startListening() {
if (isHdrEnabled() && !mIsListenerRegistered && mViewRoot.mDisplay != null) {
mViewRoot.mDisplay.registerHdrSdrRatioChangedListener(mViewRoot.mExecutor, this);
+ mIsListenerRegistered = true;
}
}
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index 7c2577f..c302126 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -193,6 +193,9 @@
/** @hide */ public static final int POINTER_ICON_VECTOR_STYLE_FILL_END =
POINTER_ICON_VECTOR_STYLE_FILL_BLUE;
+ /** @hide */ public static final float DEFAULT_POINTER_SCALE = 1f;
+ /** @hide */ public static final float LARGE_POINTER_SCALE = 2.5f;
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final int mType;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -253,7 +256,7 @@
* @hide
*/
public static @NonNull PointerIcon getLoadedSystemIcon(@NonNull Context context, int type,
- boolean useLargeIcons) {
+ boolean useLargeIcons, float pointerScale) {
if (type == TYPE_NOT_SPECIFIED) {
throw new IllegalStateException("Cannot load icon for type TYPE_NOT_SPECIFIED");
}
@@ -268,13 +271,18 @@
}
final int defStyle;
- // TODO(b/305193969): Use scaled vectors when large icons are requested.
- if (useLargeIcons) {
- defStyle = com.android.internal.R.style.LargePointer;
- } else if (android.view.flags.Flags.enableVectorCursors()) {
+ if (android.view.flags.Flags.enableVectorCursorA11ySettings()) {
defStyle = com.android.internal.R.style.VectorPointer;
} else {
- defStyle = com.android.internal.R.style.Pointer;
+ // TODO(b/346358375): Remove useLargeIcons and the legacy pointer styles when
+ // enableVectorCursorA11ySetting is rolled out.
+ if (useLargeIcons) {
+ defStyle = com.android.internal.R.style.LargePointer;
+ } else if (android.view.flags.Flags.enableVectorCursors()) {
+ defStyle = com.android.internal.R.style.VectorPointer;
+ } else {
+ defStyle = com.android.internal.R.style.Pointer;
+ }
}
TypedArray a = context.obtainStyledAttributes(null,
com.android.internal.R.styleable.Pointer,
@@ -286,11 +294,11 @@
Log.w(TAG, "Missing theme resources for pointer icon type " + type);
return type == TYPE_DEFAULT
? getSystemIcon(TYPE_NULL)
- : getLoadedSystemIcon(context, TYPE_DEFAULT, useLargeIcons);
+ : getLoadedSystemIcon(context, TYPE_DEFAULT, useLargeIcons, pointerScale);
}
final PointerIcon icon = new PointerIcon(type);
- icon.loadResource(context.getResources(), resourceId, context.getTheme());
+ icon.loadResource(context.getResources(), resourceId, context.getTheme(), pointerScale);
return icon;
}
@@ -353,7 +361,7 @@
}
PointerIcon icon = new PointerIcon(TYPE_CUSTOM);
- icon.loadResource(resources, resourceId, null);
+ icon.loadResource(resources, resourceId, null, DEFAULT_POINTER_SCALE);
return icon;
}
@@ -460,12 +468,13 @@
}
private BitmapDrawable getBitmapDrawableFromVectorDrawable(Resources resources,
- VectorDrawable vectorDrawable) {
+ VectorDrawable vectorDrawable, float pointerScale) {
// Ensure we pass the display metrics into the Bitmap constructor so that it is initialized
// with the correct density.
Bitmap bitmap = Bitmap.createBitmap(resources.getDisplayMetrics(),
- vectorDrawable.getIntrinsicWidth(),
- vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888, true /* hasAlpha */);
+ (int) (vectorDrawable.getIntrinsicWidth() * pointerScale),
+ (int) (vectorDrawable.getIntrinsicHeight() * pointerScale),
+ Bitmap.Config.ARGB_8888, true /* hasAlpha */);
Canvas canvas = new Canvas(bitmap);
vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
vectorDrawable.draw(canvas);
@@ -473,7 +482,7 @@
}
private void loadResource(@NonNull Resources resources, @XmlRes int resourceId,
- @Nullable Resources.Theme theme) {
+ @Nullable Resources.Theme theme, float pointerScale) {
final XmlResourceParser parser = resources.getXml(resourceId);
final int bitmapRes;
final float hotSpotX;
@@ -484,8 +493,10 @@
final TypedArray a = resources.obtainAttributes(
parser, com.android.internal.R.styleable.PointerIcon);
bitmapRes = a.getResourceId(com.android.internal.R.styleable.PointerIcon_bitmap, 0);
- hotSpotX = a.getDimension(com.android.internal.R.styleable.PointerIcon_hotSpotX, 0);
- hotSpotY = a.getDimension(com.android.internal.R.styleable.PointerIcon_hotSpotY, 0);
+ hotSpotX = a.getDimension(com.android.internal.R.styleable.PointerIcon_hotSpotX, 0)
+ * pointerScale;
+ hotSpotY = a.getDimension(com.android.internal.R.styleable.PointerIcon_hotSpotY, 0)
+ * pointerScale;
a.recycle();
} catch (Exception ex) {
throw new IllegalArgumentException("Exception parsing pointer icon resource.", ex);
@@ -534,7 +545,7 @@
}
if (isVectorAnimation) {
drawableFrame = getBitmapDrawableFromVectorDrawable(resources,
- (VectorDrawable) drawableFrame);
+ (VectorDrawable) drawableFrame, pointerScale);
}
mBitmapFrames[i - 1] = getBitmapFromDrawable((BitmapDrawable) drawableFrame);
}
@@ -542,7 +553,8 @@
}
if (drawable instanceof VectorDrawable) {
mDrawNativeDropShadow = true;
- drawable = getBitmapDrawableFromVectorDrawable(resources, (VectorDrawable) drawable);
+ drawable = getBitmapDrawableFromVectorDrawable(resources, (VectorDrawable) drawable,
+ pointerScale);
}
if (!(drawable instanceof BitmapDrawable)) {
throw new IllegalArgumentException("<pointer-icon> bitmap attribute must "
diff --git a/core/java/android/view/SurfaceControlRegistry.java b/core/java/android/view/SurfaceControlRegistry.java
index 127d4a7..aa3654d 100644
--- a/core/java/android/view/SurfaceControlRegistry.java
+++ b/core/java/android/view/SurfaceControlRegistry.java
@@ -342,12 +342,14 @@
return false;
}
final boolean matchName = !sCallStackDebuggingMatchName.isEmpty();
- if (matchName && (name == null
- || !sCallStackDebuggingMatchName.contains(name.toLowerCase()))) {
- // Skip if target surface doesn't match requested surface
+ if (!matchName) {
+ return true;
+ }
+ if (name == null) {
return false;
}
- return true;
+ return sCallStackDebuggingMatchName.contains(name.toLowerCase()) ||
+ name.toLowerCase().contains(sCallStackDebuggingMatchName);
}
/**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 14bb681..9bc1511 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7152,7 +7152,7 @@
public void setPendingCredentialRequest(@NonNull GetCredentialRequest request,
@NonNull OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback) {
Preconditions.checkNotNull(request, "request must not be null");
- Preconditions.checkNotNull(callback, "request must not be null");
+ Preconditions.checkNotNull(callback, "callback must not be null");
for (CredentialOption option : request.getCredentialOptions()) {
ArrayList<AutofillId> ids = option.getCandidateQueryData()
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index cb82278..a11bb78 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -203,6 +203,7 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.sysprop.DisplayProperties;
+import android.sysprop.ViewProperties;
import android.text.TextUtils;
import android.util.AndroidRuntimeException;
import android.util.DisplayMetrics;
@@ -1209,6 +1210,7 @@
Flags.enableInvalidateCheckThread();
private static boolean sSurfaceFlingerBugfixFlagValue =
com.android.graphics.surfaceflinger.flags.Flags.vrrBugfix24q4();
+ private static final boolean sEnableVrr = ViewProperties.vrr_enabled().orElse(true);
static {
sToolkitSetFrameRateReadOnlyFlagValue = toolkitSetFrameRateReadOnly();
@@ -7504,8 +7506,6 @@
final KeyEvent event = (KeyEvent)q.mEvent;
if (mView.dispatchKeyEventPreIme(event)) {
return FINISH_HANDLED;
- } else if (q.forPreImeOnly()) {
- return FINISH_NOT_HANDLED;
}
return FORWARD;
}
@@ -10002,7 +10002,6 @@
public static final int FLAG_RESYNTHESIZED = 1 << 4;
public static final int FLAG_UNHANDLED = 1 << 5;
public static final int FLAG_MODIFIED_FOR_COMPATIBILITY = 1 << 6;
- public static final int FLAG_PRE_IME_ONLY = 1 << 7;
public QueuedInputEvent mNext;
@@ -10010,13 +10009,6 @@
public InputEventReceiver mReceiver;
public int mFlags;
- public boolean forPreImeOnly() {
- if ((mFlags & FLAG_PRE_IME_ONLY) != 0) {
- return true;
- }
- return false;
- }
-
public boolean shouldSkipIme() {
if ((mFlags & FLAG_DELIVER_POST_IME) != 0) {
return true;
@@ -10043,7 +10035,6 @@
hasPrevious = flagToString("FINISHED_HANDLED", FLAG_FINISHED_HANDLED, hasPrevious, sb);
hasPrevious = flagToString("RESYNTHESIZED", FLAG_RESYNTHESIZED, hasPrevious, sb);
hasPrevious = flagToString("UNHANDLED", FLAG_UNHANDLED, hasPrevious, sb);
- hasPrevious = flagToString("FLAG_PRE_IME_ONLY", FLAG_PRE_IME_ONLY, hasPrevious, sb);
if (!hasPrevious) {
sb.append("0");
}
@@ -10100,7 +10091,7 @@
}
@UnsupportedAppUsage
- QueuedInputEvent enqueueInputEvent(InputEvent event,
+ void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately) {
QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
@@ -10139,7 +10130,6 @@
} else {
scheduleProcessInputEvents();
}
- return q;
}
private void scheduleProcessInputEvents() {
@@ -12461,45 +12451,29 @@
+ "IWindow:%s Session:%s",
mOnBackInvokedDispatcher, mBasePackageName, mWindow, mWindowSession));
}
- mOnBackInvokedDispatcher.attachToWindow(mWindowSession, mWindow, this,
+ mOnBackInvokedDispatcher.attachToWindow(mWindowSession, mWindow,
mImeBackAnimationController);
}
- /**
- * Sends {@link KeyEvent#ACTION_DOWN ACTION_DOWN} and {@link KeyEvent#ACTION_UP ACTION_UP}
- * back key events
- *
- * @param preImeOnly whether the back events should be sent to the pre-ime stage only
- * @return whether the event was handled (i.e. onKeyPreIme consumed it if preImeOnly=true)
- */
- public boolean injectBackKeyEvents(boolean preImeOnly) {
- boolean consumed;
- try {
- processingBackKey(true);
- sendBackKeyEvent(KeyEvent.ACTION_DOWN, preImeOnly);
- consumed = sendBackKeyEvent(KeyEvent.ACTION_UP, preImeOnly);
- } finally {
- processingBackKey(false);
- }
- return consumed;
- }
-
- private boolean sendBackKeyEvent(int action, boolean preImeOnly) {
+ private void sendBackKeyEvent(int action) {
long when = SystemClock.uptimeMillis();
final KeyEvent ev = new KeyEvent(when, when, action,
KeyEvent.KEYCODE_BACK, 0 /* repeat */, 0 /* metaState */,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
InputDevice.SOURCE_KEYBOARD);
- int flags = preImeOnly ? QueuedInputEvent.FLAG_PRE_IME_ONLY : 0;
- QueuedInputEvent q = enqueueInputEvent(ev, null /* receiver */, flags,
- true /* processImmediately */);
- return (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0;
+ enqueueInputEvent(ev, null /* receiver */, 0 /* flags */, true /* processImmediately */);
}
private void registerCompatOnBackInvokedCallback() {
mCompatOnBackInvokedCallback = () -> {
- injectBackKeyEvents(/* preImeOnly */ false);
+ try {
+ processingBackKey(true);
+ sendBackKeyEvent(KeyEvent.ACTION_DOWN);
+ sendBackKeyEvent(KeyEvent.ACTION_UP);
+ } finally {
+ processingBackKey(false);
+ }
};
if (mOnBackInvokedDispatcher.hasImeOnBackInvokedDispatcher()) {
Log.d(TAG, "Skip registering CompatOnBackInvokedCallback on IME dispatcher");
@@ -12952,13 +12926,13 @@
private boolean shouldSetFrameRateCategory() {
// use toolkitSetFrameRate flag to gate the change
- return mSurface.isValid() && shouldEnableDvrr();
+ return shouldEnableDvrr() && mSurface.isValid() && shouldEnableDvrr();
}
private boolean shouldSetFrameRate() {
// use toolkitSetFrameRate flag to gate the change
- return mSurface.isValid() && mPreferredFrameRate >= 0
- && shouldEnableDvrr() && !mIsFrameRateConflicted;
+ return shouldEnableDvrr() && mSurface.isValid() && mPreferredFrameRate >= 0
+ && !mIsFrameRateConflicted;
}
private boolean shouldTouchBoost(int motionEventAction, int windowType) {
@@ -12993,7 +12967,7 @@
* @param view The View with the ThreadedRenderer animation that started.
*/
public void addThreadedRendererView(View view) {
- if (!mThreadedRendererViews.contains(view)) {
+ if (shouldEnableDvrr() && !mThreadedRendererViews.contains(view)) {
mThreadedRendererViews.add(view);
}
}
@@ -13005,7 +12979,8 @@
*/
public void removeThreadedRendererView(View view) {
mThreadedRendererViews.remove(view);
- if (!mInvalidationIdleMessagePosted && sSurfaceFlingerBugfixFlagValue) {
+ if (shouldEnableDvrr()
+ && !mInvalidationIdleMessagePosted && sSurfaceFlingerBugfixFlagValue) {
mInvalidationIdleMessagePosted = true;
mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE, IDLE_TIME_MILLIS);
}
@@ -13226,7 +13201,7 @@
private boolean shouldEnableDvrr() {
// uncomment this when we are ready for enabling dVRR
- if (sToolkitFrameRateViewEnablingReadOnlyFlagValue) {
+ if (sEnableVrr && sToolkitFrameRateViewEnablingReadOnlyFlagValue) {
return sToolkitSetFrameRateReadOnlyFlagValue && isFrameRatePowerSavingsBalanced();
}
return false;
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 42bf420..7b7ead4 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -963,7 +963,7 @@
* true:
* <ul>
* <li>Activity has requested orientation more than two times within one-second timer
- * <li>Activity is not letterboxed for fixed orientation
+ * <li>Activity is not letterboxed for fixed-orientation apps
* </ul>
*
* <p>Setting this property to {@code false} informs the system that the app must be
@@ -1055,22 +1055,22 @@
* for an app to inform the system that the app should be excluded from the camera compatibility
* force rotation treatment.
*
- * <p>The camera compatibility treatment aligns orientations of portrait app window and natural
- * orientation of the device and set opposite to natural orientation for a landscape app
- * window. Mismatch between them can lead to camera issues like sideways or stretched
+ * <p>The camera compatibility treatment aligns portrait app windows with the natural
+ * orientation of the device and landscape app windows opposite the device natural orientation.
+ * Mismatch between the orientations can lead to camera issues like a sideways or stretched
* viewfinder since this is one of the strongest assumptions that apps make when they implement
- * camera previews. Since app and natural display orientations aren't guaranteed to match, the
- * rotation can cause letterboxing. The forced rotation is triggered as soon as app opens to
+ * camera previews. Since app and device natural orientations aren't guaranteed to match, the
+ * rotation can cause letterboxing. The forced rotation is triggered as soon as an app opens the
* camera and is removed once camera is closed.
*
- * <p>The camera compatibility can be enabled by device manufacturers on displays that have the
- * ignore requested orientation display setting enabled (enables compatibility mode for fixed
- * orientation on Android 12 (API level 31) or higher; see
- * <a href="https://developer.android.com/guide/practices/enhanced-letterboxing">Enhanced
- * letterboxing</a> for more details).
+ * <p>Camera compatibility can be enabled by device manufacturers on displays that have the
+ * ignore requested orientation display setting enabled, which enables compatibility mode for
+ * fixed-orientation apps on Android 12 (API level 31) or higher. See
+ * <a href="{@docRoot}guide/practices/device-compatibility-mode">Device compatibility mode</a>
+ * for more details.
*
* <p>With this property set to {@code true} or unset, the system may apply the force rotation
- * treatment to fixed orientation activities. Device manufacturers can exclude packages from the
+ * treatment to fixed-orientation activities. Device manufacturers can exclude packages from the
* treatment using their discretion to improve display compatibility.
*
* <p>With this property set to {@code false}, the system will not apply the force rotation
@@ -1093,12 +1093,12 @@
* for an app to inform the system that the app should be excluded from the activity "refresh"
* after the camera compatibility force rotation treatment.
*
- * <p>The camera compatibility treatment aligns orientations of portrait app window and natural
- * orientation of the device and set opposite to natural orientation for a landscape app
- * window. Mismatch between them can lead to camera issues like sideways or stretched
+ * <p>The camera compatibility treatment aligns portrait app windows with the natural
+ * orientation of the device and landscape app windows opposite the device natural orientation.
+ * Mismatch between the orientations can lead to camera issues like a sideways or stretched
* viewfinder since this is one of the strongest assumptions that apps make when they implement
- * camera previews. Since app and natural display orientations aren't guaranteed to match, the
- * rotation can cause letterboxing. The forced rotation is triggered as soon as app opens to
+ * camera previews. Since app and device natural orientations aren't guaranteed to match, the
+ * rotation can cause letterboxing. The forced rotation is triggered as soon as an app opens the
* camera and is removed once camera is closed.
*
* <p>Force rotation is followed by the "Refresh" of the activity by going through "resumed ->
@@ -1109,10 +1109,10 @@
* rotation.
*
* <p>The camera compatibility can be enabled by device manufacturers on displays that have the
- * ignore requested orientation display setting enabled (enables compatibility mode for fixed
- * orientation on Android 12 (API level 31) or higher; see
- * <a href="https://developer.android.com/guide/practices/enhanced-letterboxing">Enhanced
- * letterboxing</a> for more details).
+ * ignore requested orientation display setting enabled, which enables compatibility mode for
+ * fixed-orientation apps on Android 12 (API level 31) or higher. See
+ * <a href="{@docRoot}guide/practices/device-compatibility-mode">Device compatibility mode</a>
+ * for more details.
*
* <p>With this property set to {@code true} or unset, the system may "refresh" activity after
* the force rotation treatment. Device manufacturers can exclude packages from the "refresh"
@@ -1140,12 +1140,11 @@
* "stopped -> resumed".
*
* <p>The camera compatibility treatment aligns orientations of portrait app window and natural
- * orientation of the device and set opposite to natural orientation for a landscape app
- * window. Mismatch between them can lead to camera issues like sideways or stretched
- * viewfinder since this is one of the strongest assumptions that apps make when they implement
- * camera previews. Since app and natural display orientations aren't guaranteed to match, the
- * rotation can cause letterboxing. The forced rotation is triggered as soon as app opens to
- * camera and is removed once camera is closed.
+ * orientation of the device. Mismatch between the orientations can lead to camera issues like a
+ * sideways or stretched viewfinder since this is one of the strongest assumptions that apps
+ * make when they implement camera previews. Since app and natural display orientations aren't
+ * guaranteed to match, the rotation can cause letterboxing. The forced rotation is triggered as
+ * soon as app opens the camera and is removed once camera is closed.
*
* <p>Force rotation is followed by the "Refresh" of the activity by going through "resumed ->
* ... -> stopped -> ... -> resumed" cycle (by default) or "resumed -> paused -> resumed" cycle
@@ -1154,10 +1153,10 @@
* to sideways or stretching issues persisting even after force rotation.
*
* <p>The camera compatibility can be enabled by device manufacturers on displays that have the
- * ignore requested orientation display setting enabled (enables compatibility mode for fixed
- * orientation on Android 12 (API level 31) or higher; see
- * <a href="https://developer.android.com/guide/practices/enhanced-letterboxing">Enhanced
- * letterboxing</a> for more details).
+ * ignore requested orientation display setting enabled, which enables compatibility mode for
+ * fixed-orientation apps on Android 12 (API level 31) or higher. See
+ * <a href="{@docRoot}guide/practices/device-compatibility-mode">Device compatibility mode</a>
+ * for more details.
*
* <p>Device manufacturers can override packages to "refresh" via "resumed -> paused -> resumed"
* cycle using their discretion to improve display compatibility.
@@ -1203,7 +1202,7 @@
* <p>With this property set to {@code true} or unset, device manufacturers can override
* orientation for the app using their discretion to improve display compatibility.
*
- * <p>With this property set to {@code false}, device manufactured per-app override for
+ * <p>With this property set to {@code false}, device manufacturer per-app override for
* orientation won't be applied.
*
* <p><b>Syntax:</b>
@@ -1227,15 +1226,15 @@
* <p>When this compat override is enabled and while display is fixed to the landscape natural
* orientation, the orientation requested by the activity will be still respected by bounds
* resolution logic. For instance, if an activity requests portrait orientation, then activity
- * will appear in the letterbox mode for fixed orientation with the display rotated to the
- * lanscape natural orientation.
+ * appears in letterbox mode for fixed-orientation apps with the display rotated to the lanscape
+ * natural orientation.
*
* <p>The treatment is disabled by default but device manufacturers can enable the treatment
* using their discretion to improve display compatibility on displays that have the ignore
- * orientation request display setting enabled by OEMs on the device (enables compatibility mode
- * for fixed orientation on Android 12 (API level 31) or higher; see
- * <a href="https://developer.android.com/guide/practices/enhanced-letterboxing">Enhanced
- * letterboxing</a> for more details).
+ * orientation request display setting enabled by OEMs on the device, which enables
+ * compatibility mode for fixed-orientation apps on Android 12 (API level 31) or higher. See
+ * <a href="{@docRoot}guide/practices/device-compatibility-mode">Device compatibility mode</a>
+ * for more details.
*
* <p>With this property set to {@code true} or unset, the system wiil use landscape display
* orientation when the following conditions are met:
@@ -1246,7 +1245,7 @@
* <li>Device manufacturer enabled the treatment.
* </ul>
*
- * <p>With this property set to {@code false}, device manufactured per-app override for
+ * <p>With this property set to {@code false}, device manufacturer per-app override for
* display orientation won't be applied.
*
* <p><b>Syntax:</b>
@@ -1344,13 +1343,11 @@
* see {@link #PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE} to
* disable the full-screen option only.
*
- * <p>The user override is intended to improve the app experience on devices
- * that have the ignore orientation request display setting enabled by OEMs
- * (enables compatibility mode for fixed orientation on Android 12 (API
- * level 31) or higher; see
- * <a href="https://developer.android.com/guide/topics/large-screens/large-screen-compatibility-mode">
- * Large screen compatibility mode</a>
- * for more details).
+ * <p>The user override is intended to improve the app experience on devices that have the
+ * ignore orientation request display setting enabled by OEMs, which enables compatibility mode
+ * for fixed-orientation apps on Android 12 (API level 31) or higher. See
+ * <a href="{@docRoot}guide/practices/device-compatibility-mode">Device compatibility mode</a>
+ * for more details.
*
* <p>To opt out of the user aspect ratio compatibility override, add this property
* to your app manifest and set the value to {@code false}. Your app will be excluded
@@ -1383,13 +1380,11 @@
* <p>When users apply the full-screen compatibility override, the orientation
* of the activity is forced to {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_USER}.
*
- * <p>The user override is intended to improve the app experience on devices
- * that have the ignore orientation request display setting enabled by OEMs
- * (enables compatibility mode for fixed orientation on Android 12 (API
- * level 31) or higher; see
- * <a href="https://developer.android.com/guide/topics/large-screens/large-screen-compatibility-mode">
- * Large screen compatibility mode</a>
- * for more details).
+ * <p>The user override is intended to improve the app experience on devices that have the
+ * ignore orientation request display setting enabled by OEMs, which enables compatibility mode
+ * for fixed-orientation apps on Android 12 (API level 31) or higher. See
+ * <a href="{@docRoot}guide/practices/device-compatibility-mode">Device compatibility mode</a>
+ * for more details.
*
* <p>To opt out of the full-screen option of the user aspect ratio compatibility
* override, add this property to your app manifest and set the value to {@code false}.
diff --git a/core/java/android/view/autofill/AutofillFeatureFlags.java b/core/java/android/view/autofill/AutofillFeatureFlags.java
index 46b41ae..199a69a 100644
--- a/core/java/android/view/autofill/AutofillFeatureFlags.java
+++ b/core/java/android/view/autofill/AutofillFeatureFlags.java
@@ -524,7 +524,7 @@
return DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_AUTOFILL,
DEVICE_CONFIG_INCLUDE_INVISIBLE_VIEW_GROUP_IN_ASSIST_STRUCTURE,
- false);
+ true);
}
/** @hide */
@@ -548,7 +548,7 @@
return DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_AUTOFILL,
DEVICE_CONFIG_FILL_FIELDS_FROM_CURRENT_SESSION_ONLY,
- false);
+ true);
}
/**
diff --git a/core/java/android/view/autofill/AutofillId.java b/core/java/android/view/autofill/AutofillId.java
index 15ba1a1..6b60858 100644
--- a/core/java/android/view/autofill/AutofillId.java
+++ b/core/java/android/view/autofill/AutofillId.java
@@ -61,6 +61,11 @@
}
/** @hide */
+ public AutofillId(@NonNull AutofillId hostId, int virtualChildId, int sessionId) {
+ this(FLAG_IS_VIRTUAL_INT | FLAG_HAS_SESSION, hostId.mViewId, virtualChildId, sessionId);
+ }
+
+ /** @hide */
@TestApi
public AutofillId(@NonNull AutofillId hostId, long virtualChildId, int sessionId) {
this(FLAG_IS_VIRTUAL_LONG | FLAG_HAS_SESSION, hostId.mViewId, virtualChildId, sessionId);
@@ -236,9 +241,9 @@
public String toString() {
final StringBuilder builder = new StringBuilder().append(mViewId);
if (isVirtualInt()) {
- builder.append(':').append(mVirtualIntId);
+ builder.append(":i").append(mVirtualIntId);
} else if (isVirtualLong()) {
- builder.append(':').append(mVirtualLongId);
+ builder.append(":l").append(mVirtualLongId);
}
if (hasSession()) {
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 9cc4191..0d4c556 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -193,6 +193,7 @@
@RequiresFeature(PackageManager.FEATURE_AUTOFILL)
public final class AutofillManager {
+ private static final boolean DBG = false;
private static final String TAG = "AutofillManager";
/**
@@ -2027,7 +2028,31 @@
if (!hasAutofillFeature()) {
return;
}
+ if (DBG) {
+ Log.v(TAG, "notifyValueChanged() called with virtualId:" + virtualId + " value:"
+ + value);
+ }
synchronized (mLock) {
+ if (mLastAutofilledData != null) {
+ AutofillId id = new AutofillId(view.getAutofillId(), virtualId, mSessionId);
+ if (mLastAutofilledData.containsKey(id)) {
+ if (Objects.equals(mLastAutofilledData.get(id), value)) {
+ // Indicates that the view was autofilled
+ if (sDebug) {
+ Log.v(TAG, "notifyValueChanged() virtual view autofilled successfully:"
+ + virtualId + " value:" + value);
+ }
+ try {
+ mService.setViewAutofilled(mSessionId, id, mContext.getUserId());
+ } catch (RemoteException e) {
+ // The failure could be a consequence of something going wrong on the
+ // server side. Do nothing here since it's just logging, but it's
+ // possible follow-up actions may fail.
+ Log.w(TAG, "RemoteException caught but ignored " + e);
+ }
+ }
+ }
+ }
if (!mEnabled || !isActiveLocked()) {
if (sVerbose) {
Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() + ":" + virtualId
@@ -2985,16 +3010,34 @@
mLastAutofilledData.put(view.getAutofillId(), targetValue);
}
view.setAutofilled(true, hideHighlight);
+ if (sDebug) {
+ Log.d(TAG, "View " + view.getAutofillId() + " autofilled synchronously.");
+ }
try {
mService.setViewAutofilled(mSessionId, view.getAutofillId(), mContext.getUserId());
} catch (RemoteException e) {
// The failure could be a consequence of something going wrong on the server side.
// Do nothing here since it's just logging, but it's possible follow-up actions may
// fail.
+ Log.w(TAG, "Unable to log due to " + e);
+ }
+ } else {
+ if (sDebug) {
+ Log.d(TAG, "View " + view.getAutofillId() + " " + view.getClass().toString()
+ + " from " + view.getClass().getPackageName()
+ + " : didn't fill in synchronously. It may fill asynchronously.");
}
}
}
+ /**
+ * Returns String with text "null" if the object is null, or the actual string represented by
+ * the object.
+ */
+ private @NonNull String getString(Object obj) {
+ return obj == null ? "null" : obj.toString();
+ }
+
private void onGetCredentialException(int sessionId, AutofillId id, String errorType,
String errorMsg) {
synchronized (mLock) {
@@ -3114,6 +3157,10 @@
ArrayList<AutofillId> failedIds = new ArrayList<>();
+ if (mLastAutofilledData == null) {
+ mLastAutofilledData = new ParcelableMap(itemCount);
+ }
+
for (int i = 0; i < itemCount; i++) {
final AutofillId id = ids.get(i);
final AutofillValue value = values.get(i);
@@ -3126,6 +3173,9 @@
failedIds.add(id);
continue;
}
+ // Mark the view as to be autofilled with 'value'
+ mLastAutofilledData.put(id, value);
+
if (id.isVirtualInt()) {
if (virtualValues == null) {
// Most likely there will be just one view with virtual children.
@@ -3139,12 +3189,6 @@
}
valuesByParent.put(id.getVirtualChildIntId(), value);
} else {
- // Mark the view as to be autofilled with 'value'
- if (mLastAutofilledData == null) {
- mLastAutofilledData = new ParcelableMap(itemCount - i);
- }
- mLastAutofilledData.put(id, value);
-
view.autofill(value);
// Set as autofilled if the values match now, e.g. when the value was updated
@@ -3575,40 +3619,14 @@
// isCredential field indicates that the developer might be calling Credman, and we should
// suppress autofill dialogs. But it is not a good enough indicator that there is a valid
// credman option.
- if (view.isCredential()) {
- return true;
- }
- return containsAutofillHintPrefix(view, View.AUTOFILL_HINT_CREDENTIAL_MANAGER);
+ return view.isCredential() || isCredmanRequested(view);
}
private boolean isCredmanRequested(View view) {
if (view == null) {
return false;
}
- if (view.getViewCredentialHandler() != null) {
- return true;
- }
-
- String[] hints = view.getAutofillHints();
- if (hints == null) {
- return false;
- }
- // if hint starts with 'credential=', then we assume that there is a valid
- // credential option set by the client.
- return containsAutofillHintPrefix(view, View.AUTOFILL_HINT_CREDENTIAL_MANAGER + "=");
- }
-
- private boolean containsAutofillHintPrefix(View view, String prefix) {
- String[] hints = view.getAutofillHints();
- if (hints == null) {
- return false;
- }
- for (String hint : hints) {
- if (hint != null && hint.startsWith(prefix)) {
- return true;
- }
- }
- return false;
+ return view.getViewCredentialHandler() != null;
}
/**
@@ -4308,14 +4326,16 @@
if (mIsTrackedSaveView && mVisibleTrackedIds.isEmpty()) {
if (sVerbose) {
- Log.v(TAG, "No more visible ids. Invisible = " + mInvisibleTrackedIds);
+ Log.v(TAG, "No more visible tracked save ids. Invisible = "
+ + mInvisibleTrackedIds);
}
finishSessionLocked(/* commitReason= */ COMMIT_REASON_VIEW_CHANGED);
}
if (mVisibleDialogTrackedIds.isEmpty()) {
if (sVerbose) {
- Log.v(TAG, "No more visible ids. Invisible = " + mInvisibleDialogTrackedIds);
+ Log.v(TAG, "No more visible tracked fill dialog ids. Invisible = "
+ + mInvisibleDialogTrackedIds);
}
processNoVisibleTrackedAllViews();
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index bcef37f..d74867c 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -366,6 +366,14 @@
"enable_content_protection_receiver";
/**
+ * Whether AssistContent snapshot should be sent on activity start.
+ *
+ * @hide
+ */
+ public static final String DEVICE_CONFIG_ENABLE_ACTIVITY_START_ASSIST_CONTENT =
+ "enable_activity_start_assist_content";
+
+ /**
* Sets the size of the in-memory ring buffer for the content protection flow.
*
* @hide
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 2d4bb90..0c63e58 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -129,6 +129,7 @@
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.Collections;
@@ -2630,13 +2631,17 @@
return false;
}
if (useDelegation) {
+ WeakReference<Executor> executorRef = new WeakReference<>(executor);
+ WeakReference<Consumer<Boolean>> callbackRef = new WeakReference<>(callback);
if (useCallback) {
IBooleanListener listener = new IBooleanListener.Stub() {
@Override
public void onResult(boolean value) {
- executor.execute(() -> {
- callback.accept(value);
- });
+ Executor executor = executorRef.get();
+ Consumer<Boolean> callback = callbackRef.get();
+ if (executor != null && callback != null) {
+ executor.execute(() -> callback.accept(value));
+ }
}
};
if (!IInputMethodManagerGlobalInvoker.acceptStylusHandwritingDelegationAsync(
diff --git a/core/java/android/widget/CursorTreeAdapter.java b/core/java/android/widget/CursorTreeAdapter.java
old mode 100755
new mode 100644
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
old mode 100755
new mode 100644
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
old mode 100755
new mode 100644
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 15f9cff8..3c5623f 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -1044,11 +1044,6 @@
public int getActionTag() {
return SET_PENDING_INTENT_TEMPLATE_TAG;
}
-
- @Override
- public void visitUris(@NonNull Consumer<Uri> visitor) {
- mPendingIntentTemplate.visitUris(visitor);
- }
}
/**
@@ -1528,11 +1523,6 @@
public int getActionTag() {
return SET_REMOTE_VIEW_ADAPTER_INTENT_TAG;
}
-
- @Override
- public void visitUris(@NonNull Consumer<Uri> visitor) {
- mIntent.visitUris(visitor);
- }
}
/**
@@ -1611,11 +1601,6 @@
public int getActionTag() {
return SET_ON_CLICK_RESPONSE_TAG;
}
-
- @Override
- public void visitUris(@NonNull Consumer<Uri> visitor) {
- mResponse.visitUris(visitor);
- }
}
/** Helper action to configure handwriting delegation via {@link PendingIntent}. */
@@ -1663,11 +1648,6 @@
public int getActionTag() {
return SET_ON_STYLUS_HANDWRITING_RESPONSE_TAG;
}
-
- @Override
- public void visitUris(@NonNull Consumer<Uri> visitor) {
- mPendingIntent.visitUris(visitor);
- }
}
/**
@@ -1738,11 +1718,6 @@
public int getActionTag() {
return SET_ON_CHECKED_CHANGE_RESPONSE_TAG;
}
-
- @Override
- public void visitUris(@NonNull Consumer<Uri> visitor) {
- mResponse.visitUris(visitor);
- }
}
/** @hide **/
@@ -2302,10 +2277,6 @@
final Icon icon = (Icon) getParameterValue(null);
if (icon != null) visitIconUri(icon, visitor);
break;
- case INTENT:
- final Intent intent = (Intent) getParameterValue(null);
- if (intent != null) intent.visitUris(visitor);
- break;
// TODO(b/281044385): Should we do anything about type BUNDLE?
}
}
@@ -7226,20 +7197,6 @@
mElementNames = parcel.createStringArrayList();
}
- /**
- * See {@link RemoteViews#visitUris(Consumer)}.
- *
- * @hide
- */
- public void visitUris(@NonNull Consumer<Uri> visitor) {
- if (mPendingIntent != null) {
- mPendingIntent.visitUris(visitor);
- }
- if (mFillIntent != null) {
- mFillIntent.visitUris(visitor);
- }
- }
-
private void handleViewInteraction(
View v,
InteractionHandler handler) {
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
old mode 100755
new mode 100644
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index fd3837f..f7e0ec8 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -14051,6 +14051,9 @@
@Override
public void autofill(AutofillValue value) {
+ if (android.view.autofill.Helper.sVerbose) {
+ Log.v(LOG_TAG, "autofill() called on textview for id:" + getAutofillId());
+ }
if (!isTextAutofillable()) {
Log.w(LOG_TAG, "cannot autofill non-editable TextView: " + this);
return;
diff --git a/core/java/android/window/BackNavigationInfo.java b/core/java/android/window/BackNavigationInfo.java
index f24bc74..57bded7 100644
--- a/core/java/android/window/BackNavigationInfo.java
+++ b/core/java/android/window/BackNavigationInfo.java
@@ -23,6 +23,7 @@
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.graphics.Color;
+import android.graphics.Rect;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -113,6 +114,8 @@
private final CustomAnimationInfo mCustomAnimationInfo;
private final int mLetterboxColor;
+ @NonNull
+ private final Rect mTouchableRegion;
/**
* Create a new {@link BackNavigationInfo} instance.
@@ -128,7 +131,8 @@
boolean isPrepareRemoteAnimation,
boolean isAnimationCallback,
@Nullable CustomAnimationInfo customAnimationInfo,
- int letterboxColor) {
+ int letterboxColor,
+ @Nullable Rect touchableRegion) {
mType = type;
mOnBackNavigationDone = onBackNavigationDone;
mOnBackInvokedCallback = onBackInvokedCallback;
@@ -136,6 +140,7 @@
mAnimationCallback = isAnimationCallback;
mCustomAnimationInfo = customAnimationInfo;
mLetterboxColor = letterboxColor;
+ mTouchableRegion = new Rect(touchableRegion);
}
private BackNavigationInfo(@NonNull Parcel in) {
@@ -146,6 +151,7 @@
mAnimationCallback = in.readBoolean();
mCustomAnimationInfo = in.readTypedObject(CustomAnimationInfo.CREATOR);
mLetterboxColor = in.readInt();
+ mTouchableRegion = in.readTypedObject(Rect.CREATOR);
}
/** @hide */
@@ -158,6 +164,7 @@
dest.writeBoolean(mAnimationCallback);
dest.writeTypedObject(mCustomAnimationInfo, flags);
dest.writeInt(mLetterboxColor);
+ dest.writeTypedObject(mTouchableRegion, flags);
}
/**
@@ -206,6 +213,16 @@
public int getLetterboxColor() {
return mLetterboxColor;
}
+
+ /**
+ * @return The app window region where the client can handle touch event.
+ * @hide
+ */
+ @NonNull
+ public Rect getTouchableRegion() {
+ return mTouchableRegion;
+ }
+
/**
* Callback to be called when the back preview is finished in order to notify the server that
* it can clean up the resources created for the animation.
@@ -402,6 +419,7 @@
private boolean mAnimationCallback = false;
private int mLetterboxColor = Color.TRANSPARENT;
+ private Rect mTouchableRegion;
/**
* @see BackNavigationInfo#getType()
@@ -478,6 +496,13 @@
}
/**
+ * @param rect Non-empty for frame of current focus window.
+ */
+ public Builder setTouchableRegion(Rect rect) {
+ mTouchableRegion = new Rect(rect);
+ return this;
+ }
+ /**
* Builds and returns an instance of {@link BackNavigationInfo}
*/
public BackNavigationInfo build() {
@@ -486,7 +511,8 @@
mPrepareRemoteAnimation,
mAnimationCallback,
mCustomAnimationInfo,
- mLetterboxColor);
+ mLetterboxColor,
+ mTouchableRegion);
}
}
}
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 4c993c2..4ca64e7 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -37,7 +37,6 @@
import android.view.IWindowSession;
import android.view.ImeBackAnimationController;
import android.view.MotionEvent;
-import android.view.ViewRootImpl;
import androidx.annotation.VisibleForTesting;
@@ -50,7 +49,6 @@
import java.util.HashMap;
import java.util.Objects;
import java.util.TreeMap;
-import java.util.function.BooleanSupplier;
import java.util.function.Supplier;
/**
@@ -70,7 +68,6 @@
public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
private IWindowSession mWindowSession;
private IWindow mWindow;
- private ViewRootImpl mViewRoot;
@VisibleForTesting
public final BackTouchTracker mTouchTracker = new BackTouchTracker();
@VisibleForTesting
@@ -137,12 +134,10 @@
* is attached a window.
*/
public void attachToWindow(@NonNull IWindowSession windowSession, @NonNull IWindow window,
- @Nullable ViewRootImpl viewRoot,
@Nullable ImeBackAnimationController imeBackAnimationController) {
synchronized (mLock) {
mWindowSession = windowSession;
mWindow = window;
- mViewRoot = viewRoot;
mImeBackAnimationController = imeBackAnimationController;
if (!mAllCallbacks.isEmpty()) {
setTopOnBackInvokedCallback(getTopCallback());
@@ -156,7 +151,6 @@
clear();
mWindow = null;
mWindowSession = null;
- mViewRoot = null;
mImeBackAnimationController = null;
}
}
@@ -182,6 +176,8 @@
return;
}
if (callback instanceof ImeOnBackInvokedDispatcher.ImeOnBackInvokedCallback) {
+ // Fall back to compat back key injection if legacy back behaviour should be used.
+ if (!isOnBackInvokedCallbackEnabled()) return;
if (callback instanceof ImeOnBackInvokedDispatcher.DefaultImeOnBackAnimationCallback
&& mImeBackAnimationController != null) {
// register ImeBackAnimationController instead to play predictive back animation
@@ -304,14 +300,6 @@
}
}
- private boolean callOnKeyPreIme() {
- if (mViewRoot != null && !isOnBackInvokedCallbackEnabled(mViewRoot.mContext)) {
- return mViewRoot.injectBackKeyEvents(/*preImeOnly*/ true);
- } else {
- return false;
- }
- }
-
private void setTopOnBackInvokedCallback(@Nullable OnBackInvokedCallback callback) {
if (mWindowSession == null || mWindow == null) {
return;
@@ -320,8 +308,8 @@
OnBackInvokedCallbackInfo callbackInfo = null;
if (callback != null) {
int priority = mAllCallbacks.get(callback);
- final IOnBackInvokedCallback iCallback = new OnBackInvokedCallbackWrapper(callback,
- mTouchTracker, mProgressAnimator, mHandler, this::callOnKeyPreIme);
+ final IOnBackInvokedCallback iCallback = new OnBackInvokedCallbackWrapper(
+ callback, mTouchTracker, mProgressAnimator, mHandler);
callbackInfo = new OnBackInvokedCallbackInfo(
iCallback,
priority,
@@ -411,20 +399,16 @@
private final BackTouchTracker mTouchTracker;
@NonNull
private final Handler mHandler;
- @NonNull
- private final BooleanSupplier mOnKeyPreIme;
OnBackInvokedCallbackWrapper(
@NonNull OnBackInvokedCallback callback,
@NonNull BackTouchTracker touchTracker,
@NonNull BackProgressAnimator progressAnimator,
- @NonNull Handler handler,
- @NonNull BooleanSupplier onKeyPreIme) {
+ @NonNull Handler handler) {
mCallback = new WeakReference<>(callback);
mTouchTracker = touchTracker;
mProgressAnimator = progressAnimator;
mHandler = handler;
- mOnKeyPreIme = onKeyPreIme;
}
@Override
@@ -451,7 +435,16 @@
}
@Override
- public void onBackProgressed(BackMotionEvent backEvent) { }
+ public void onBackProgressed(BackMotionEvent backEvent) {
+ // This is only called in some special cases such as when activity embedding is active
+ // or when the activity is letterboxed. Otherwise mProgressAnimator#onBackProgressed is
+ // called from WindowOnBackInvokedDispatcher#onMotionEvent
+ mHandler.post(() -> {
+ if (getBackAnimationCallback() != null) {
+ mProgressAnimator.onBackProgressed(backEvent);
+ }
+ });
+ }
@Override
public void onBackCancelled() {
@@ -467,7 +460,6 @@
public void onBackInvoked() throws RemoteException {
mHandler.post(() -> {
mTouchTracker.reset();
- if (consumedByOnKeyPreIme()) return;
boolean isInProgress = mProgressAnimator.isBackAnimationInProgress();
final OnBackInvokedCallback callback = mCallback.get();
if (callback == null) {
@@ -489,30 +481,6 @@
});
}
- private boolean consumedByOnKeyPreIme() {
- final OnBackInvokedCallback callback = mCallback.get();
- if (callback instanceof ImeBackAnimationController
- || callback instanceof ImeOnBackInvokedDispatcher.ImeOnBackInvokedCallback) {
- // call onKeyPreIme API if the current callback is an IME callback and the app has
- // not set enableOnBackInvokedCallback="false"
- try {
- boolean consumed = mOnKeyPreIme.getAsBoolean();
- if (consumed) {
- // back event intercepted by app in onKeyPreIme -> cancel the IME animation.
- final OnBackAnimationCallback animationCallback =
- getBackAnimationCallback();
- if (animationCallback != null) {
- mProgressAnimator.onBackCancelled(animationCallback::onBackCancelled);
- }
- return true;
- }
- } catch (Exception e) {
- Log.d(TAG, "Failed to call onKeyPreIme", e);
- }
- }
- return false;
- }
-
@Override
public void setTriggerBack(boolean triggerBack) throws RemoteException {
mTouchTracker.setTriggerBack(triggerBack);
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index daf2fe3..ca125da 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -106,3 +106,17 @@
description: "Whether to apply Camera Compat treatment to fixed-orientation apps in desktop windowing mode"
bug: "314952133"
}
+
+flag {
+ name: "enable_task_stack_observer_in_shell"
+ namespace: "lse_desktop_experience"
+ description: "Introduces a new observer in shell to track the task stack."
+ bug: "341932484"
+}
+
+flag {
+ name: "enable_desktop_windowing_size_constraints"
+ namespace: "lse_desktop_experience"
+ description: "Whether to enable min/max window size constraints when resizing a window in desktop windowing mode"
+ bug: "327589741"
+}
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index 87ede4a..0a4762d 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -180,3 +180,14 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ namespace: "windowing_sdk"
+ name: "rear_display_disable_force_desktop_system_decorations"
+ description: "Block system decorations from being added to a rear display when desktop mode is forced"
+ bug: "346103150"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 98d6ec6..920981e 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -923,7 +923,7 @@
mSystemWindowInsets = insets.getSystemWindowInsets();
mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top,
- mSystemWindowInsets.right, mSystemWindowInsets.bottom);
+ mSystemWindowInsets.right, 0);
resetButtonBar();
@@ -952,7 +952,7 @@
if (mSystemWindowInsets != null) {
mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top,
- mSystemWindowInsets.right, mSystemWindowInsets.bottom);
+ mSystemWindowInsets.right, 0);
}
}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index c14a6c1..63ff598 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1227,7 +1227,10 @@
requestApplyInsets();
}
}
- if (insets != null) {
+ if (insets != null && (consumedLeft > 0
+ || consumedTop > 0
+ || consumedRight > 0
+ || consumedBottom > 0)) {
insets = insets.inset(consumedLeft, consumedTop, consumedRight, consumedBottom);
}
}
diff --git a/core/java/com/android/internal/util/function/pooled/OmniFunction.java b/core/java/com/android/internal/util/function/pooled/OmniFunction.java
old mode 100755
new mode 100644
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambda.java b/core/java/com/android/internal/util/function/pooled/PooledLambda.java
old mode 100755
new mode 100644
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
old mode 100755
new mode 100644
diff --git a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java
index cb0d62e..d1c4d46 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java
@@ -80,11 +80,13 @@
/**
* The delay in milliseconds to next repaint -1 = not needed 0 = asap
+ *
* @return delay in milliseconds to next repaint or -1
*/
public int needsRepaint() {
return mDocument.needsRepaint();
}
+
/**
* Returns true if the document can be displayed given this version of the player
*
@@ -99,8 +101,7 @@
@Override
public String toString() {
return "Document{\n"
- + mDocument
- + '}';
+ + mDocument + '}';
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
index cc1f3dd..7423a16 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
@@ -16,6 +16,7 @@
package com.android.internal.widget.remotecompose.player;
import android.content.Context;
+import android.graphics.Color;
import android.util.AttributeSet;
import android.util.Log;
import android.view.ViewGroup;
@@ -97,6 +98,7 @@
LayoutParams.MATCH_PARENT);
HorizontalScrollView horizontalScrollView =
new HorizontalScrollView(getContext());
+ horizontalScrollView.setBackgroundColor(Color.TRANSPARENT);
horizontalScrollView.setFillViewport(true);
horizontalScrollView.addView(mInner, layoutParamsInner);
LayoutParams layoutParams = new LayoutParams(
@@ -113,6 +115,7 @@
LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT);
ScrollView scrollView = new ScrollView(getContext());
+ scrollView.setBackgroundColor(Color.TRANSPARENT);
scrollView.setFillViewport(true);
scrollView.addView(mInner, layoutParamsInner);
LayoutParams layoutParams = new LayoutParams(
@@ -136,7 +139,9 @@
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
+ setBackgroundColor(Color.TRANSPARENT);
mInner = new RemoteComposeCanvas(context, attrs, defStyleAttr);
+ mInner.setBackgroundColor(Color.TRANSPARENT);
addView(mInner, layoutParams);
}
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index f914bee..d32486c 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -203,55 +203,52 @@
return true;
}
-static void pointerCoordsToNative(JNIEnv* env, jobject pointerCoordsObj,
- float xOffset, float yOffset, PointerCoords* outRawPointerCoords) {
- outRawPointerCoords->clear();
- outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_X,
- env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.x) - xOffset);
- outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_Y,
- env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.y) - yOffset);
- outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
- env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.pressure));
- outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_SIZE,
- env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.size));
- outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR,
- env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.touchMajor));
- outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR,
- env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.touchMinor));
- outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR,
- env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.toolMajor));
- outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR,
- env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.toolMinor));
- outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
- env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.orientation));
- outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X,
- env->GetFloatField(pointerCoordsObj,
- gPointerCoordsClassInfo.relativeX));
- outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y,
- env->GetFloatField(pointerCoordsObj,
- gPointerCoordsClassInfo.relativeY));
- outRawPointerCoords->isResampled =
- env->GetBooleanField(pointerCoordsObj, gPointerCoordsClassInfo.isResampled);
+static PointerCoords pointerCoordsToNative(JNIEnv* env, jobject pointerCoordsObj) {
+ PointerCoords out{};
+ out.setAxisValue(AMOTION_EVENT_AXIS_X,
+ env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.x));
+ out.setAxisValue(AMOTION_EVENT_AXIS_Y,
+ env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.y));
+ out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
+ env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.pressure));
+ out.setAxisValue(AMOTION_EVENT_AXIS_SIZE,
+ env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.size));
+ out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR,
+ env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.touchMajor));
+ out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR,
+ env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.touchMinor));
+ out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR,
+ env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.toolMajor));
+ out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR,
+ env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.toolMinor));
+ out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
+ env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.orientation));
+ out.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X,
+ env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.relativeX));
+ out.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y,
+ env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.relativeY));
+ out.isResampled = env->GetBooleanField(pointerCoordsObj, gPointerCoordsClassInfo.isResampled);
BitSet64 bits =
BitSet64(env->GetLongField(pointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisBits));
if (!bits.isEmpty()) {
- jfloatArray valuesArray = jfloatArray(env->GetObjectField(pointerCoordsObj,
- gPointerCoordsClassInfo.mPackedAxisValues));
+ jfloatArray valuesArray = jfloatArray(
+ env->GetObjectField(pointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisValues));
if (valuesArray) {
- jfloat* values = static_cast<jfloat*>(
- env->GetPrimitiveArrayCritical(valuesArray, NULL));
+ jfloat* values =
+ static_cast<jfloat*>(env->GetPrimitiveArrayCritical(valuesArray, NULL));
uint32_t index = 0;
do {
uint32_t axis = bits.clearFirstMarkedBit();
- outRawPointerCoords->setAxisValue(axis, values[index++]);
+ out.setAxisValue(axis, values[index++]);
} while (!bits.isEmpty());
env->ReleasePrimitiveArrayCritical(valuesArray, values, JNI_ABORT);
env->DeleteLocalRef(valuesArray);
}
}
+ return out;
}
static jfloatArray obtainPackedAxisValuesArray(JNIEnv* env, uint32_t minSize,
@@ -303,14 +300,13 @@
env->SetLongField(outPointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisBits, outBits);
}
-static void pointerPropertiesToNative(JNIEnv* env, jobject pointerPropertiesObj,
- PointerProperties* outPointerProperties) {
- outPointerProperties->clear();
- outPointerProperties->id = env->GetIntField(pointerPropertiesObj,
- gPointerPropertiesClassInfo.id);
- const int32_t toolType = env->GetIntField(pointerPropertiesObj,
- gPointerPropertiesClassInfo.toolType);
- outPointerProperties->toolType = static_cast<ToolType>(toolType);
+static PointerProperties pointerPropertiesToNative(JNIEnv* env, jobject pointerPropertiesObj) {
+ PointerProperties out{};
+ out.id = env->GetIntField(pointerPropertiesObj, gPointerPropertiesClassInfo.id);
+ const int32_t toolType =
+ env->GetIntField(pointerPropertiesObj, gPointerPropertiesClassInfo.toolType);
+ out.toolType = static_cast<ToolType>(toolType);
+ return out;
}
static void pointerPropertiesFromNative(JNIEnv* env, const PointerProperties* pointerProperties,
@@ -343,15 +339,21 @@
event = std::make_unique<MotionEvent>();
}
- PointerProperties pointerProperties[pointerCount];
- PointerCoords rawPointerCoords[pointerCount];
+ ui::Transform transform;
+ transform.set(xOffset, yOffset);
+ const ui::Transform inverseTransform = transform.inverse();
+
+ std::vector<PointerProperties> pointerProperties;
+ pointerProperties.reserve(pointerCount);
+ std::vector<PointerCoords> rawPointerCoords;
+ rawPointerCoords.reserve(pointerCount);
for (jint i = 0; i < pointerCount; i++) {
jobject pointerPropertiesObj = env->GetObjectArrayElement(pointerPropertiesObjArray, i);
if (!pointerPropertiesObj) {
return 0;
}
- pointerPropertiesToNative(env, pointerPropertiesObj, &pointerProperties[i]);
+ pointerProperties.emplace_back(pointerPropertiesToNative(env, pointerPropertiesObj));
env->DeleteLocalRef(pointerPropertiesObj);
jobject pointerCoordsObj = env->GetObjectArrayElement(pointerCoordsObjArray, i);
@@ -359,19 +361,24 @@
jniThrowNullPointerException(env, "pointerCoords");
return 0;
}
- pointerCoordsToNative(env, pointerCoordsObj, xOffset, yOffset, &rawPointerCoords[i]);
+ rawPointerCoords.emplace_back(pointerCoordsToNative(env, pointerCoordsObj));
+ PointerCoords& coords = rawPointerCoords.back();
+ if (coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION) != 0.f) {
+ flags |= AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION |
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION;
+ }
+ MotionEvent::calculateTransformedCoordsInPlace(coords, source, flags, inverseTransform);
env->DeleteLocalRef(pointerCoordsObj);
}
- ui::Transform transform;
- transform.set(xOffset, yOffset);
- ui::Transform identityTransform;
+ static const ui::Transform kIdentityTransform;
event->initialize(InputEvent::nextId(), deviceId, source, ui::LogicalDisplayId{displayId},
INVALID_HMAC, action, 0, flags, edgeFlags, metaState, buttonState,
static_cast<MotionClassification>(classification), transform, xPrecision,
yPrecision, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, downTimeNanos,
- eventTimeNanos, pointerCount, pointerProperties, rawPointerCoords);
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, kIdentityTransform, downTimeNanos,
+ eventTimeNanos, pointerCount, pointerProperties.data(),
+ rawPointerCoords.data());
return reinterpret_cast<jlong>(event.release());
}
@@ -391,7 +398,10 @@
return;
}
- PointerCoords rawPointerCoords[pointerCount];
+ const ui::Transform inverseTransform = event->getTransform().inverse();
+
+ std::vector<PointerCoords> rawPointerCoords;
+ rawPointerCoords.reserve(pointerCount);
for (size_t i = 0; i < pointerCount; i++) {
jobject pointerCoordsObj = env->GetObjectArrayElement(pointerCoordsObjArray, i);
@@ -399,12 +409,13 @@
jniThrowNullPointerException(env, "pointerCoords");
return;
}
- pointerCoordsToNative(env, pointerCoordsObj, event->getRawXOffset(), event->getRawYOffset(),
- &rawPointerCoords[i]);
+ rawPointerCoords.emplace_back(pointerCoordsToNative(env, pointerCoordsObj));
+ MotionEvent::calculateTransformedCoordsInPlace(rawPointerCoords.back(), event->getSource(),
+ event->getFlags(), inverseTransform);
env->DeleteLocalRef(pointerCoordsObj);
}
- event->addSample(eventTimeNanos, rawPointerCoords);
+ event->addSample(eventTimeNanos, rawPointerCoords.data());
event->setMetaState(event->getMetaState() | metaState);
}
@@ -685,13 +696,15 @@
static jint android_view_MotionEvent_nativeGetFlags(CRITICAL_JNI_PARAMS_COMMA jlong nativePtr) {
MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return event->getFlags();
+ // Prevent private flags from being used in Java.
+ return event->getFlags() & ~AMOTION_EVENT_PRIVATE_FLAG_MASK;
}
static void android_view_MotionEvent_nativeSetFlags(CRITICAL_JNI_PARAMS_COMMA jlong nativePtr,
jint flags) {
MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- event->setFlags(flags);
+ // Prevent private flags from being used from Java.
+ event->setFlags(flags & ~AMOTION_EVENT_PRIVATE_FLAG_MASK);
}
static jint android_view_MotionEvent_nativeGetEdgeFlags(CRITICAL_JNI_PARAMS_COMMA jlong nativePtr) {
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index e831a7d..5365838 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -358,7 +358,8 @@
jobject stats =
env->NewObject(gTransactionStatsClassInfo.clazz, gTransactionStatsClassInfo.ctor,
- latchTime, presentFence.get());
+ latchTime,
+ static_cast<jlong>(reinterpret_cast<uintptr_t>(presentFence.get())));
env->CallVoidMethod(mTransactionCompletedListenerObject, gConsumerClassInfo.accept, stats);
env->DeleteLocalRef(stats);
DieIfException(env, "Uncaught exception in TransactionCompletedListener.");
diff --git a/core/proto/android/providers/settings/system.proto b/core/proto/android/providers/settings/system.proto
index 1233069..6a0ec1d 100644
--- a/core/proto/android/providers/settings/system.proto
+++ b/core/proto/android/providers/settings/system.proto
@@ -126,6 +126,7 @@
option (android.msg_privacy).dest = DEST_EXPLICIT;
optional SettingProto pointer_fill_style = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto pointer_scale = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional Pointer pointer = 37;
optional SettingProto pointer_speed = 18 [ (android.privacy).dest = DEST_AUTOMATIC ];
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 09ffdf3..c71f9bd 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3249,16 +3249,20 @@
<permission android:name="android.permission.INTERACT_ACROSS_PROFILES"
android:protectionLevel="signature|appop" />
- <!-- Allows applications to access profiles with ACCESS_HIDDEN_PROFILES user property
- <p>Protection level: normal
- @FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles") -->
+ <!-- Allows applications to access profiles with
+ {@code android.content.pm.UserProperties#PROFILE_API_VISIBILITY_HIDDEN} user property, e.g.
+ {@link android.os.UserManager#USER_TYPE_PROFILE_PRIVATE}.
+ <p>Protection level: normal
+ @FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles") -->
<permission android:name="android.permission.ACCESS_HIDDEN_PROFILES"
android:label="@string/permlab_accessHiddenProfile"
android:description="@string/permdesc_accessHiddenProfile"
android:protectionLevel="normal" />
- <!-- @SystemApi @hide Allows privileged applications to get details about hidden profile
- users.
+ <!-- @SystemApi @hide Allows privileged applications to get details about profiles with
+ {@code android.content.pm.UserProperties#PROFILE_API_VISIBILITY_HIDDEN} user property, e.g.
+ {@link android.os.UserManager#USER_TYPE_PROFILE_PRIVATE}. Removes extra requirements such
+ as having {@link android.app.role.RoleManager#ROLE_HOME} role for LauncherApps APIs.
@FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles") -->
<permission
android:name="android.permission.ACCESS_HIDDEN_PROFILES_FULL"
@@ -3322,13 +3326,18 @@
<!-- Allows an application to manage device policy relating to time.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
- APIs protected by this permission on users different to the calling user.-->
+ APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
+ -->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_TIME"
android:protectionLevel="internal|role" />
<!-- Allows an application to set the grant state of runtime permissions on packages.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS"
android:protectionLevel="internal|role" />
@@ -3336,6 +3345,8 @@
<!-- Allows an application to manage the identity of the managing organization.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY"
android:protectionLevel="internal|role" />
@@ -3344,6 +3355,8 @@
active policy.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE"
android:protectionLevel="internal|role" />
@@ -3351,6 +3364,8 @@
<!-- Allows an application to manage backup service policy.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_BACKUP_SERVICE"
android:protectionLevel="internal|role" />
@@ -3358,6 +3373,8 @@
<!-- Allows an application to manage lock task policy.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCK_TASK"
android:protectionLevel="internal|role" />
@@ -3365,6 +3382,8 @@
<!-- Allows an application to manage policy regarding modifying applications.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL"
android:protectionLevel="internal|role" />
@@ -3372,6 +3391,8 @@
<!-- Allows an application to manage installing from unknown sources policy.
<p>MANAGE_SECURITY_CRITICAL_DEVICE_POLICY_ACROSS_USERS is required to call APIs protected
by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES"
android:protectionLevel="internal|role" />
@@ -3379,6 +3400,8 @@
<!-- Allows an application to manage application restrictions.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_APP_RESTRICTIONS"
android:protectionLevel="internal|role" />
@@ -3386,6 +3409,8 @@
<!-- Allows an application to manage calling policy.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_CALLS"
android:protectionLevel="internal|role" />
@@ -3393,6 +3418,8 @@
<!-- Allows an application to manage debugging features policy.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES"
android:protectionLevel="internal|role" />
@@ -3400,6 +3427,8 @@
<!-- Allows an application to manage policy preventing users from modifying users.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
APIs protected by this permission on users different to the calling user
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_MODIFY_USERS"
android:protectionLevel="internal|role" />
@@ -3407,6 +3436,8 @@
<!-- Allows an application to manage safe boot policy.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_SAFE_BOOT"
android:protectionLevel="internal|role" />
@@ -3415,6 +3446,8 @@
enable and disable the microphone.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call
APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_MICROPHONE"
android:protectionLevel="internal|role" />
@@ -3423,6 +3456,8 @@
enable and disable the camera.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call
APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_CAMERA"
android:protectionLevel="internal|role" />
@@ -3430,6 +3465,8 @@
<!-- Allows an application to manage policy related to keyguard.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_KEYGUARD"
android:protectionLevel="internal|role" />
@@ -3437,6 +3474,8 @@
<!-- Allows an application to set policy related to account management.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call
APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT"
android:protectionLevel="internal|role" />
@@ -3444,6 +3483,8 @@
<!-- Allows an application to set policy related to hiding and suspending packages.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call
APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_PACKAGE_STATE"
android:protectionLevel="internal|role" />
@@ -3452,17 +3493,24 @@
challenge on current user.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_RESET_PASSWORD"
android:protectionLevel="internal|role" />
- <!-- Allows an application to set policy related to the status bar.-->
+ <!-- Allows an application to set policy related to the status bar.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
+ -->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_STATUS_BAR"
android:protectionLevel="internal|role" />
<!-- Allows an application to set policy related to bluetooth.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_BLUETOOTH"
android:protectionLevel="internal|role" />
@@ -3470,6 +3518,8 @@
<!-- Allows an application to set policy related to fun.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_FUN"
android:protectionLevel="internal|role" />
@@ -3477,6 +3527,8 @@
<!-- Allows an application to set policy related to airplane mode.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call
APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_AIRPLANE_MODE"
android:protectionLevel="internal|role" />
@@ -3484,6 +3536,8 @@
<!-- Allows an application to set policy related to mobile networks.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_MOBILE_NETWORK"
android:protectionLevel="internal|role" />
@@ -3491,6 +3545,8 @@
<!-- Allows an application to set policy related to physical media.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA"
android:protectionLevel="internal|role" />
@@ -3498,6 +3554,8 @@
<!-- Allows an application to set policy related to sms.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_SMS"
android:protectionLevel="internal|role" />
@@ -3505,6 +3563,8 @@
<!-- Allows an application to set policy related to usb file transfers.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER"
android:protectionLevel="internal|role" />
@@ -3512,6 +3572,8 @@
<!-- Allows an application to set policy related to lock credentials.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS"
android:protectionLevel="internal|role" />
@@ -3519,6 +3581,8 @@
<!-- Allows an application to set policy related to Wifi.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_WIFI"
android:protectionLevel="internal|role" />
@@ -3526,6 +3590,8 @@
<!-- Allows an application to set policy related to screen capture.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_SCREEN_CAPTURE"
android:protectionLevel="internal|role" />
@@ -3533,6 +3599,8 @@
<!-- Allows an application to set policy related to input methods.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_INPUT_METHODS"
android:protectionLevel="internal|role" />
@@ -3541,6 +3609,8 @@
private DNS.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS"
android:protectionLevel="internal|role" />
@@ -3548,6 +3618,8 @@
<!-- Allows an application to set policy related to the default sms application.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_DEFAULT_SMS"
android:protectionLevel="internal|role" />
@@ -3555,6 +3627,8 @@
<!-- Allows an application to set policy related to profiles.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_PROFILES"
android:protectionLevel="internal|role" />
@@ -3563,6 +3637,8 @@
cross-profile copy and paste).
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_PROFILE_INTERACTION"
android:protectionLevel="internal|role" />
@@ -3570,6 +3646,8 @@
<!-- Allows an application to set policy related to VPNs.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_VPN"
android:protectionLevel="internal|role" />
@@ -3577,6 +3655,8 @@
<!-- Allows an application to set policy related to audio output.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_AUDIO_OUTPUT"
android:protectionLevel="internal|role" />
@@ -3584,6 +3664,8 @@
<!-- Allows an application to set policy related to the display.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_DISPLAY"
android:protectionLevel="internal|role" />
@@ -3591,6 +3673,8 @@
<!-- Allows an application to set policy related to location.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCATION"
android:protectionLevel="internal|role" />
@@ -3598,6 +3682,8 @@
<!-- Allows an application to set policy related to factory reset.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_FACTORY_RESET"
android:protectionLevel="internal|role" />
@@ -3605,6 +3691,8 @@
<!-- Allows an application to set policy related to the wallpaper.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_WALLPAPER"
android:protectionLevel="internal|role" />
@@ -3612,6 +3700,8 @@
<!-- Allows an application to set policy related to the usage of the contents of the screen.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_SCREEN_CONTENT"
android:protectionLevel="internal|role" />
@@ -3619,6 +3709,8 @@
<!-- Allows an application to set policy related to system dialogs.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS"
android:protectionLevel="internal|role" />
@@ -3626,6 +3718,8 @@
<!-- Allows an application to set policy related to users running in the background.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_RUN_IN_BACKGROUND"
android:protectionLevel="internal|role" />
@@ -3633,6 +3727,8 @@
<!-- Allows an application to set policy related to printing.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_PRINTING"
android:protectionLevel="internal|role" />
@@ -3641,12 +3737,16 @@
nearby streaming).
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION"
android:protectionLevel="internal|role" />
<!-- Allows an application to set policy related to <a
href="https://www.threadgroup.org">Thread</a> network.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
@FlaggedApi("com.android.net.thread.platform.flags.thread_user_restriction_enabled")
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_THREAD_NETWORK"
@@ -3654,6 +3754,8 @@
<!-- Allows an application to set policy related to sending assist content to a
privileged app such as the Assistant app.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
@FlaggedApi("android.app.admin.flags.assist_content_user_restriction_enabled")
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_ASSIST_CONTENT"
@@ -3662,6 +3764,8 @@
<!-- Allows an application to set policy related to windows.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_WINDOWS"
android:protectionLevel="internal|role" />
@@ -3669,6 +3773,8 @@
<!-- Allows an application to set policy related to locale.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCALE"
android:protectionLevel="internal|role" />
@@ -3676,6 +3782,8 @@
<!-- Allows an application to set policy related to autofill.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_AUTOFILL"
android:protectionLevel="internal|role" />
@@ -3683,6 +3791,8 @@
<!-- Allows an application to set policy related to users.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_USERS"
android:protectionLevel="internal|role" />
@@ -3690,6 +3800,8 @@
<!-- Allows an application to set policy related to certificates.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_CERTIFICATES"
android:protectionLevel="internal|role" />
@@ -3697,6 +3809,8 @@
<!-- Allows an application to set policy related to override APNs.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_OVERRIDE_APN"
android:protectionLevel="internal|role" />
@@ -3704,6 +3818,8 @@
<!-- Allows an application to set policy related to security logging.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_SECURITY_LOGGING"
android:protectionLevel="internal|role" />
@@ -3719,6 +3835,8 @@
<!-- Allows an application to set policy related to system updates.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_SYSTEM_UPDATES"
android:protectionLevel="internal|role" />
@@ -3726,6 +3844,8 @@
<!-- Allows an application query system updates.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES"
android:protectionLevel="internal|role" />
@@ -3733,6 +3853,8 @@
<!-- Allows an application to set policy related to private DNS.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_PRIVATE_DNS"
android:protectionLevel="internal|role" />
@@ -3740,6 +3862,8 @@
<!-- Allows an application to set policy related to settings.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_SETTINGS"
android:protectionLevel="internal|role" />
@@ -3747,17 +3871,24 @@
<!-- Allows an application to set policy related to network logging.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_NETWORK_LOGGING"
android:protectionLevel="internal|role" />
- <!-- Allows an application to set policy related to usb data signalling.-->
+ <!-- Allows an application to set policy related to usb data signalling.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
+ -->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING"
android:protectionLevel="internal|role" />
<!-- Allows an application to set policy related to suspending personal apps.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_SUSPEND_PERSONAL_APPS"
android:protectionLevel="internal|role" />
@@ -3765,13 +3896,17 @@
<!-- Allows an application to set policy related to keeping uninstalled packages.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
required to call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_KEEP_UNINSTALLED_PACKAGES"
android:protectionLevel="internal|role" />
<!-- Allows an application to manage policy related to accessibility.
- <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
- APIs protected by this permission on users different to the calling user.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to
+ call APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACCESSIBILITY"
android:protectionLevel="internal|role" />
@@ -3779,6 +3914,8 @@
<!-- Allows an application to manage policy related to common criteria mode.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE"
android:protectionLevel="internal|role" />
@@ -3786,6 +3923,8 @@
<!-- Allows an application to manage policy related to metered data.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_METERED_DATA"
android:protectionLevel="internal|role" />
@@ -3793,6 +3932,8 @@
<!-- Allows an application to set a network-independent global HTTP proxy.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_PROXY"
android:protectionLevel="internal|role" />
@@ -3800,6 +3941,8 @@
<!-- Allows an application to request bugreports with user consent.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_BUGREPORT"
android:protectionLevel="internal|role" />
@@ -3807,6 +3950,8 @@
<!-- Allows an application to manage policy related to application user data.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_APP_USER_DATA"
android:protectionLevel="internal|role" />
@@ -3815,6 +3960,8 @@
permission.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCK"
android:protectionLevel="internal|role" />
@@ -3830,6 +3977,8 @@
<!-- Allows an application to manage policy related to system apps.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_SYSTEM_APPS"
android:protectionLevel="internal|role" />
@@ -3837,16 +3986,23 @@
<!-- Allows an application to manage policy related to wiping data.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call
APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_WIPE_DATA"
android:protectionLevel="internal|role" />
<!-- Allows an application to manage policy related to the Memory Tagging Extension (MTE).
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_MTE"
android:protectionLevel="internal|role" />
- <!-- Allows an application to manage policy related to device identifiers. -->
+ <!-- Allows an application to manage policy related to device identifiers.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
+ -->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_DEVICE_IDENTIFIERS"
android:protectionLevel="internal|role" />
@@ -3859,24 +4015,33 @@
<!-- Allows an application to set policy related to subscriptions downloaded by an admin.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
- APIs protected by this permission on users different to the calling user.
- @FlaggedApi("android.app.admin.flags.esim_management_enabled") -->
+ APIs protected by this permission on users different to the calling user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
+ @FlaggedApi("android.app.admin.flags.esim_management_enabled")
+ -->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS"
android:protectionLevel="internal|role" />
<!-- Allows an application to manage policy related to block package uninstallation.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
@FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled")
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL"
android:protectionLevel="internal|role" />
<!-- Allows an application to manage policy related to camera toggle.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
@FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled")
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_CAMERA_TOGGLE"
android:protectionLevel="internal|role" />
<!-- Allows an application to manage policy related to microphone toggle.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
@FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled")
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE"
@@ -3885,16 +4050,21 @@
<!-- Allows an application to set device policies outside the current user
that are critical for securing data within the current user.
<p>Holding this permission allows the use of other held MANAGE_DEVICE_POLICY_*
- permissions across all users on the device provided they are required for securing data
- within the current user.-->
+ permissions across all users on the device provided they are required for securing data
+ within the current user.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
+ -->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL"
android:protectionLevel="internal|role" />
<!-- Allows an application to set device policies outside the current user
that are required for securing device ownership without accessing user data.
<p>Holding this permission allows the use of other held MANAGE_DEVICE_POLICY_*
- permissions across all users on the device provided they do not grant access to user
- data. -->
+ permissions across all users on the device provided they do not grant access to user data.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
+ -->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS"
android:protectionLevel="internal|role" />
@@ -3902,7 +4072,10 @@
<p>Fuller form of {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS}
that removes the restriction on accessing user data.
<p>Holding this permission allows the use of any other held MANAGE_DEVICE_POLICY_*
- permissions across all users on the device.-->
+ permissions across all users on the device.
+ <p>Protection level: internal|role
+ <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
+ -->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL"
android:protectionLevel="internal|role" />
@@ -8194,6 +8367,17 @@
<permission android:name="android.permission.SETUP_FSVERITY"
android:protectionLevel="signature|privileged"/>
+ <!--
+ @TestApi
+ Signature permission reserved for testing. This should never be used to
+ gate any actual functionality.
+ <p>
+ Protection level: signature
+ @hide
+ -->
+ <permission android:name="android.permission.RESERVED_FOR_TESTING_SIGNATURE"
+ android:protectionLevel="signature"/>
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
diff --git a/core/res/res/drawable/floating_popup_background_light.xml b/core/res/res/drawable/floating_popup_background.xml
similarity index 84%
rename from core/res/res/drawable/floating_popup_background_light.xml
rename to core/res/res/drawable/floating_popup_background.xml
index 767140d..99acedf 100644
--- a/core/res/res/drawable/floating_popup_background_light.xml
+++ b/core/res/res/drawable/floating_popup_background.xml
@@ -16,8 +16,9 @@
*/
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
- <solid android:color="@color/background_floating_material_light" />
+ <solid android:color="?androidprv:attr/materialColorSurfaceContainerHighest"/>
<corners android:radius="?android:attr/dialogCornerRadius" />
</shape>
diff --git a/core/res/res/drawable/floating_popup_background_dark.xml b/core/res/res/drawable/floating_popup_background_dark.xml
deleted file mode 100644
index c4b4448..0000000
--- a/core/res/res/drawable/floating_popup_background_dark.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* Copyright 2015, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <solid android:color="@color/background_floating_material_dark" />
- <corners android:radius="?android:attr/dialogCornerRadius" />
-</shape>
-
diff --git a/core/res/res/drawable/ic_swipe_down.xml b/core/res/res/drawable/ic_swipe_down.xml
new file mode 100644
index 0000000..15712d6
--- /dev/null
+++ b/core/res/res/drawable/ic_swipe_down.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M180,600L40,460L82,418L152,488Q146,461 143,434Q140,407 140,380Q140,298 167,221Q194,144 245,80L288,123Q245,179 222.5,244.5Q200,310 200,380Q200,406 203,431.5Q206,457 213,482L278,418L320,460L180,600ZM658,833Q635,841 611.5,840.5Q588,840 566,829L304,707L322,667Q332,647 350,634.5Q368,622 390,620L458,615L346,308Q340,292 347,277.5Q354,263 370,257Q386,251 400.5,258Q415,265 421,281L569,688L469,695L600,756Q607,759 615,759.5Q623,760 630,758L787,701Q818,690 832,659.5Q846,629 835,598L780,448Q774,432 781,417.5Q788,403 804,397Q820,391 834.5,398Q849,405 855,421L910,571Q933,634 905.5,693.5Q878,753 815,776L658,833ZM568,568L514,417Q508,401 515,386.5Q522,372 538,366Q554,360 568.5,367Q583,374 589,390L644,540L568,568ZM681,527L640,414Q634,398 641,383.5Q648,369 664,363Q680,357 694.5,364Q709,371 715,387L756,499L681,527ZM689,605L689,605L689,605Q689,605 689,605Q689,605 689,605L689,605Q689,605 689,605Q689,605 689,605L689,605L689,605Z"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/immersive_cling_bg_circ.xml b/core/res/res/drawable/immersive_cling_bg.xml
similarity index 67%
rename from core/res/res/drawable/immersive_cling_bg_circ.xml
rename to core/res/res/drawable/immersive_cling_bg.xml
index 4731bbd..de29c32 100644
--- a/core/res/res/drawable/immersive_cling_bg_circ.xml
+++ b/core/res/res/drawable/immersive_cling_bg.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2015 The Android Open Source Project
+ ~ Copyright (C) 2024 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -15,12 +15,10 @@
~ limitations under the License
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="oval" >
-
- <solid android:color="@color/white" />
-
- <size
- android:height="56dp"
- android:width="56dp" />
-
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+ <corners
+ android:bottomLeftRadius="28dp"
+ android:bottomRightRadius="28dp"/>
+ <solid android:color="?androidprv:attr/materialColorSurfaceContainer" />
</shape>
diff --git a/core/res/res/drawable/immersive_cling_btn_bg.xml b/core/res/res/drawable/immersive_cling_btn_bg.xml
new file mode 100644
index 0000000..df49e38
--- /dev/null
+++ b/core/res/res/drawable/immersive_cling_btn_bg.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android">
+ <ripple android:color="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <solid android:color="@android:color/white" />
+ <corners android:radius="28dp" />
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="rectangle">
+ <corners android:radius="28dp" />
+ <solid android:color="?android:attr/colorAccent" />
+ </shape>
+ </item>
+ </ripple>
+</inset>
\ No newline at end of file
diff --git a/core/res/res/drawable/immersive_cling_light_bg_circ.xml b/core/res/res/drawable/immersive_cling_light_bg_circ.xml
deleted file mode 100644
index df5d5ad..0000000
--- a/core/res/res/drawable/immersive_cling_light_bg_circ.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="oval" >
-
- <solid android:color="#80ffffff" />
-
- <size
- android:height="76dp"
- android:width="76dp" />
-
-</shape>
diff --git a/core/res/res/layout/floating_popup_container.xml b/core/res/res/layout/floating_popup_container.xml
index 776a35d..96f0909 100644
--- a/core/res/res/layout/floating_popup_container.xml
+++ b/core/res/res/layout/floating_popup_container.xml
@@ -24,4 +24,4 @@
android:elevation="@android:dimen/text_edit_floating_toolbar_elevation"
android:focusable="true"
android:focusableInTouchMode="true"
- android:background="?attr/floatingToolbarPopupBackgroundDrawable"/>
+ android:background="@drawable/floating_popup_background"/>
diff --git a/core/res/res/layout/floating_popup_menu_button.xml b/core/res/res/layout/floating_popup_menu_button.xml
index e4c2a34..0b3861c 100644
--- a/core/res/res/layout/floating_popup_menu_button.xml
+++ b/core/res/res/layout/floating_popup_menu_button.xml
@@ -16,6 +16,7 @@
*/
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
@@ -53,7 +54,7 @@
android:ellipsize="end"
android:fontFamily="@*android:string/config_bodyFontFamily"
android:textSize="@dimen/floating_toolbar_text_size"
- android:textColor="?attr/floatingToolbarForegroundColor"
+ android:textColor="?androidprv:attr/materialColorOnSurface"
android:background="@null"
android:focusable="false"
android:focusableInTouchMode="false"
diff --git a/core/res/res/layout/floating_popup_overflow_button.xml b/core/res/res/layout/floating_popup_overflow_button.xml
index 12e2000..a51836b 100644
--- a/core/res/res/layout/floating_popup_overflow_button.xml
+++ b/core/res/res/layout/floating_popup_overflow_button.xml
@@ -16,6 +16,7 @@
*/
-->
<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:id="@+id/overflow"
android:layout_width="@dimen/floating_toolbar_menu_image_button_width"
android:layout_height="@dimen/floating_toolbar_height"
@@ -25,4 +26,4 @@
android:paddingBottom="@dimen/floating_toolbar_menu_image_button_vertical_padding"
android:scaleType="centerInside"
android:background="?attr/actionBarItemBackground"
- android:tint="?attr/floatingToolbarForegroundColor" />
+ android:tint="?androidprv:attr/materialColorOnSurface" />
diff --git a/core/res/res/layout/immersive_mode_cling.xml b/core/res/res/layout/immersive_mode_cling.xml
index 9fd615d..2cde9e6 100644
--- a/core/res/res/layout/immersive_mode_cling.xml
+++ b/core/res/res/layout/immersive_mode_cling.xml
@@ -14,79 +14,67 @@
limitations under the License.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="?android:attr/colorAccent"
+ android:background="@android:drawable/immersive_cling_bg"
android:gravity="center_vertical"
- android:paddingBottom="24dp">
+ android:padding="24dp">
- <FrameLayout
- android:id="@+id/immersive_cling_chevron"
- android:layout_width="76dp"
- android:layout_height="76dp"
- android:layout_marginTop="-24dp"
- android:layout_centerHorizontal="true">
-
- <ImageView
- android:id="@+id/immersive_cling_back_bg_light"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="center"
- android:src="@drawable/immersive_cling_light_bg_circ" />
-
- <ImageView
- android:id="@+id/immersive_cling_back_bg"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="center"
- android:src="@drawable/immersive_cling_bg_circ" />
-
- <ImageView
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingTop="8dp"
- android:scaleType="center"
- android:src="@drawable/ic_expand_more_48dp"
- android:tint="?android:attr/colorAccent"/>
- </FrameLayout>
+ <!-- The top margin of this icon can be adjusted to push the content down to prevent overlapping
+ with the display cutout. -->
+ <ImageView
+ android:id="@+id/immersive_cling_icon"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:layout_centerHorizontal="true"
+ android:scaleType="fitXY"
+ android:src="@drawable/ic_swipe_down"
+ android:tint="?android:attr/colorAccent"
+ android:tintMode="src_in" />
<TextView
android:id="@+id/immersive_cling_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_below="@id/immersive_cling_chevron"
- android:paddingEnd="48dp"
- android:paddingStart="48dp"
- android:paddingTop="40dp"
+ android:layout_below="@id/immersive_cling_icon"
+ android:layout_marginTop="20dp"
+ android:gravity="center_horizontal"
android:text="@string/immersive_cling_title"
- android:textColor="@android:color/white"
- android:textSize="24sp" />
+ android:textColor="?androidprv:attr/materialColorOnSurface"
+ android:textSize="24sp"
+ android:fontFamily="google-sans" />
<TextView
android:id="@+id/immersive_cling_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/immersive_cling_title"
- android:paddingEnd="48dp"
- android:paddingStart="48dp"
- android:paddingTop="12.6dp"
+ android:paddingTop="14dp"
+ android:gravity="center_horizontal"
android:text="@string/immersive_cling_description"
- android:textColor="@android:color/white"
- android:textSize="16sp" />
+ android:textColor="?androidprv:attr/materialColorOnSurfaceVariant"
+ android:textSize="14sp"
+ android:fontFamily="google-sans" />
<Button
android:id="@+id/ok"
- style="@style/Widget.Material.Button.Borderless"
+ style="@style/Widget.Material.Button.Borderless.Colored"
+ android:background="@drawable/immersive_cling_btn_bg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_below="@+id/immersive_cling_description"
- android:layout_marginEnd="40dp"
- android:layout_marginTop="18dp"
- android:paddingEnd="8dp"
- android:paddingStart="8dp"
+ android:layout_marginTop="24dp"
+ android:paddingStart="18dp"
+ android:paddingEnd="18dp"
+ android:minWidth="48dp"
+ android:minHeight="48dp"
android:text="@string/immersive_cling_positive"
- android:textColor="@android:color/white"
- android:textSize="14sp" />
-
+ android:textColor="?androidprv:attr/materialColorOnPrimary"
+ android:textAllCaps="false"
+ android:textSize="14sp"
+ android:textFontWeight="500"
+ android:fontFamily="google-sans" />
</RelativeLayout>
diff --git a/core/res/res/layout/subscription_item_layout.xml b/core/res/res/layout/subscription_item_layout.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/layout/text_edit_suggestion_container_material.xml b/core/res/res/layout/text_edit_suggestion_container_material.xml
index 34e7bc8..d6e1e9d 100644
--- a/core/res/res/layout/text_edit_suggestion_container_material.xml
+++ b/core/res/res/layout/text_edit_suggestion_container_material.xml
@@ -23,7 +23,7 @@
android:id="@+id/suggestionWindowContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="?android:attr/floatingToolbarPopupBackgroundDrawable"
+ android:background="@drawable/floating_popup_background"
android:elevation="@android:dimen/text_edit_floating_toolbar_elevation"
android:layout_margin="@android:dimen/text_edit_floating_toolbar_margin"
android:orientation="vertical"
diff --git a/core/res/res/values-af/donottranslate-cldr.xml b/core/res/res/values-af/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 704d442..18425fe 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privaat ruimte"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Kloon"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Gemeenskaplik"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privaat ruimte"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Sensitiewe kennisgewinginhoud is versteek"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Appinhoud is weens sekuriteit van skermdeling verberg"</string>
diff --git a/core/res/res/values-am/donottranslate-cldr.xml b/core/res/res/values-am/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index d1e18da..965160e 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"የግል ቦታ"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"አባዛ"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"የጋራ"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"የግል ቦታ"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"አደገኛ የማሳወቂያ ይዘት ተደብቋል"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ለደኅንነት ሲባል የመተግበሪያ ይዘት ከማያ ገጽ ማጋራት ተደብቋል"</string>
diff --git a/core/res/res/values-ar-rEG/donottranslate-cldr.xml b/core/res/res/values-ar-rEG/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-ar/donottranslate-cldr.xml b/core/res/res/values-ar/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 5ef18af..3e396e0 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -2411,8 +2411,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"المساحة الخاصة"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"نسخة طبق الأصل"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"ملف شخصي مشترك"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"المساحة الخاصّة"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"تم إخفاء المحتوى الحساس في الإشعار"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"تم إخفاء محتوى التطبيق بعد تفعيل ميزة \"مشاركة الشاشة\" للحفاظ على أمانك"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 5443f12..5f9f16a 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1865,7 +1865,7 @@
<string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string>
<string name="mediasize_japanese_l" msgid="1326765321473431817">"L"</string>
<string name="mediasize_unknown_portrait" msgid="3817016220446495613">"অজ্ঞাত প\'ৰ্ট্ৰেইট"</string>
- <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"অজ্ঞাত লেণ্ডস্কেইপ"</string>
+ <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"অজ্ঞাত লেণ্ডস্কে’প"</string>
<string name="write_fail_reason_cancelled" msgid="2344081488493969190">"বাতিল কৰা হ’ল"</string>
<string name="write_fail_reason_cannot_write" msgid="432118118378451508">"সমল লিখাত আসোঁৱাহ"</string>
<string name="reason_unknown" msgid="5599739807581133337">"অজ্ঞাত"</string>
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"প্ৰাইভেট স্পে’চ"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ক্ল’ন"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"সম্প্ৰদায়ৰ সৈতে জড়িত"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"প্ৰাইভেট স্পে’চ"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"সংবেদনশীল জাননী লুকুওৱা হৈছে"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"সুৰক্ষাৰ বাবে এপৰ সমল স্ক্ৰীণ শ্বেয়াৰ কৰাৰ পৰা লুকুৱাই ৰখা হৈছে"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index b9ec742..c4327e7 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Məxfi sahə"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Kommunal"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Şəxsi sahə"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Həssas bildiriş kontenti gizlədildi"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Güvənlik üçün tətbiq kontenti ekran paylaşımından gizlədildi"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 858075b..2b81e1c 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -2408,8 +2408,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privatan prostor"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klonirano"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Zajedničko"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privatan prostor"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Osetljiv sadržaj obaveštenja je skriven"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Sadržaj aplikacije je skriven za deljenje sadržaja ekrana zbog bezbednosti"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 2739cb2..7d1f6be 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -2409,8 +2409,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Прыватная прастора"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клон"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Супольны"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Прыватная прастора"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Канфідэнцыяльнае змесціва ў апавяшчэннях схавана"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Змесціва праграмы выключана з абагульвання экрана ў мэтах бяспекі"</string>
diff --git a/core/res/res/values-bg/donottranslate-cldr.xml b/core/res/res/values-bg/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 0d4cadc..a2f0364 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Частно пространство"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клониране"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Общи"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Частно пространство"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Деликатното съдържание в известието е скрито"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Съдържанието на приложението е скрито от функцията за споделяне на екрана от съображения за сигурност"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 03fba4b..e21b5c0 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"প্রাইভেট স্পেস"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ক্লোন"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"কমিউনাল"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"প্রাইভেট স্পেস"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"সংবেদনশীল বিজ্ঞপ্তির কন্টেন্ট লুকানো আছে"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"নিরাপত্তার জন্য স্ক্রিন শেয়ার করা থেকে লুকানো অ্যাপের কন্টেন্ট"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 6ce10c0..2f285f1 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -2408,8 +2408,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privatni prostor"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Opće"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privatni prostor"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Sakriven je osjetljiv sadržaj obavještenja"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Sadržaj aplikacije je sakriven od dijeljenja ekrana radi sigurnosti"</string>
diff --git a/core/res/res/values-ca/donottranslate-cldr.xml b/core/res/res/values-ca/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 4b1af34..48ba090c 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -2408,8 +2408,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espai privat"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clon"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Comunitari"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espai privat"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"S\'ha amagat contingut sensible de les notificacions"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Contingut de l\'aplicació amagat de la compartició de pantalla per motius de seguretat"</string>
diff --git a/core/res/res/values-cs/donottranslate-cldr.xml b/core/res/res/values-cs/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index cc4bd3c..64ccaf5 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -2409,8 +2409,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Soukromý prostor"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Komunální"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Soukromý prostor"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Obsah citlivých oznámení je skrytý"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Obsah aplikace je z bezpečnostních důvodů při sdílení obrazovky skryt"</string>
diff --git a/core/res/res/values-da/donottranslate-cldr.xml b/core/res/res/values-da/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 665ea17..a499afc 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privat område"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Fælles"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privat område"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Følsomt indhold i notifikationen er skjult"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Af sikkerhedsmæssige årsager vises appindhold ikke ved skærmdeling"</string>
diff --git a/core/res/res/values-de/donottranslate-cldr.xml b/core/res/res/values-de/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index cd637e1..464a5373 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Vertrauliches Profil"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Gemeinsam genutzt"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Vertrauliches Profil"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Vertrauliche Benachrichtigungsinhalte ausgeblendet"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App-Inhalte werden aus Sicherheitsgründen bei der Bildschirmfreigabe ausgeblendet"</string>
diff --git a/core/res/res/values-el/donottranslate-cldr.xml b/core/res/res/values-el/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 09ecc2c..98ce03c 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Ιδιωτικός χώρος"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Κλώνος"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Κοινόχρηστο"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Ιδιωτικός χώρος"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Έγινε απόκρυψη της ειδοποίησης ευαίσθητου περιεχομένου"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Για λόγους ασφάλειας, έγινε απόκρυψη του περιεχομένου της εφαρμογής από την κοινή χρήση οθόνης"</string>
diff --git a/core/res/res/values-en-rAU/donottranslate-cldr.xml b/core/res/res/values-en-rAU/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 20e391d..1d2fc4d 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Communal"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Private space"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Sensitive notification content hidden"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App content hidden from screen share for security"</string>
diff --git a/core/res/res/values-en-rCA/donottranslate-cldr.xml b/core/res/res/values-en-rCA/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 7f33f6a..c03bb3c 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Communal"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Private space"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Sensitive notification content hidden"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App content hidden from screen share for security"</string>
diff --git a/core/res/res/values-en-rGB/donottranslate-cldr.xml b/core/res/res/values-en-rGB/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index b7fedbe..436d7ae 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Communal"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Private space"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Sensitive notification content hidden"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App content hidden from screen share for security"</string>
diff --git a/core/res/res/values-en-rIE/donottranslate-cldr.xml b/core/res/res/values-en-rIE/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-en-rIN/donottranslate-cldr.xml b/core/res/res/values-en-rIN/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index b83a7cf..d34ed3f6e 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Communal"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Private space"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Sensitive notification content hidden"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App content hidden from screen share for security"</string>
diff --git a/core/res/res/values-en-rNZ/donottranslate-cldr.xml b/core/res/res/values-en-rNZ/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-en-rUS/donottranslate-cldr.xml b/core/res/res/values-en-rUS/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index d358e5e..c2c107c 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Communal"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Private space"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Sensitive notification content hidden"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App content hidden from screen share for security"</string>
diff --git a/core/res/res/values-en-rZA/donottranslate-cldr.xml b/core/res/res/values-en-rZA/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-es-rCO/donottranslate-cldr.xml b/core/res/res/values-es-rCO/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-es-rCR/donottranslate-cldr.xml b/core/res/res/values-es-rCR/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-es-rEC/donottranslate-cldr.xml b/core/res/res/values-es-rEC/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-es-rGT/donottranslate-cldr.xml b/core/res/res/values-es-rGT/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-es-rHN/donottranslate-cldr.xml b/core/res/res/values-es-rHN/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-es-rMX/donottranslate-cldr.xml b/core/res/res/values-es-rMX/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-es-rNI/donottranslate-cldr.xml b/core/res/res/values-es-rNI/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-es-rPA/donottranslate-cldr.xml b/core/res/res/values-es-rPA/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-es-rPE/donottranslate-cldr.xml b/core/res/res/values-es-rPE/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-es-rSV/donottranslate-cldr.xml b/core/res/res/values-es-rSV/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-es-rUS/donottranslate-cldr.xml b/core/res/res/values-es-rUS/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 45ea7ee..df7deac 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -2408,8 +2408,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espacio privado"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clon"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Compartido"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espacio privado"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Se ocultó contenido sensible de la notificación"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Se ocultó el contenido de la app durante el uso compartido de la pantalla por motivos de seguridad"</string>
diff --git a/core/res/res/values-es/donottranslate-cldr.xml b/core/res/res/values-es/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 19c5f84..e22430f 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -717,7 +717,7 @@
<string name="face_acquired_too_right" msgid="6245286514593540859">"Mueve el teléfono hacia la izquierda"</string>
<string name="face_acquired_too_left" msgid="9201762240918405486">"Mueve el teléfono hacia la derecha"</string>
<string name="face_acquired_poor_gaze" msgid="4427153558773628020">"Mira de forma más directa al dispositivo."</string>
- <string name="face_acquired_not_detected" msgid="1057966913397548150">"No se puede detectar tu cara. Sujeta el teléfono a la altura de los ojos."</string>
+ <string name="face_acquired_not_detected" msgid="1057966913397548150">"No se detecta tu cara. Sujeta el teléfono a la altura de los ojos."</string>
<string name="face_acquired_too_much_motion" msgid="8199691445085189528">"El teléfono se mueve demasiado. Mantenlo quieto."</string>
<string name="face_acquired_recalibrate" msgid="8724013080976469746">"Vuelve a registrar tu cara."</string>
<string name="face_acquired_too_different" msgid="4505278456634706967">"Cara no reconocida. Inténtalo de nuevo."</string>
@@ -2408,8 +2408,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espacio privado"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clon"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Común"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espacio privado"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Contenido sensible de la notificación oculto"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Contenido de la aplicación oculto en pantalla compartida por motivos de seguridad"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 46c7341..979079d 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privaatne ruum"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Kloon"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Ühine"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privaatne ruum"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Märguande delikaatne sisu peideti"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Rakenduse sisu on ekraani jagamises turvalisuse huvides peidetud"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 3c47f68..06855f5 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -190,7 +190,7 @@
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="4562226280528716090">"<xliff:g id="CONTENT_TYPE">%s</xliff:g> gehiegi ezabatzen saiatu zara."</string>
<string name="low_memory" product="tablet" msgid="5557552311566179924">"Tabletaren memoria beteta dago. Tokia egiteko, ezabatu fitxategi batzuk."</string>
<string name="low_memory" product="watch" msgid="3479447988234030194">"Erlojuaren memoria beteta dago. Tokia egiteko, ezabatu fitxategi batzuk."</string>
- <string name="low_memory" product="tv" msgid="6663680413790323318">"Android TV gailuaren memoria beteta dago. Tokia egiteko, ezabatu fitxategi batzuk."</string>
+ <string name="low_memory" product="tv" msgid="6663680413790323318">"Android TV gailuko biltegia beteta dago. Tokia egiteko, ezabatu fitxategi batzuk."</string>
<string name="low_memory" product="default" msgid="2539532364144025569">"Telefonoaren memoria beteta dago. Tokia egiteko, ezabatu fitxategi batzuk."</string>
<string name="ssl_ca_cert_warning" msgid="7233573909730048571">"{count,plural, =1{Autoritate ziurtagiri-emaile bat dago instalatuta}other{Autoritate ziurtagiri-emaile bat baino gehiago daude instalatuta}}"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4961102218216815242">"Hirugarren alderdi ezezagun baten arabera"</string>
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Eremu pribatua"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klona"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Partekatua"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Eremu pribatua"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Jakinarazpenaren kontuzko edukia ezkutatu da"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Aplikazioko edukia ezkutatu egin da pantaila partekatzeko eginbidetik, segurtasuna bermatzeko"</string>
diff --git a/core/res/res/values-fa/donottranslate-cldr.xml b/core/res/res/values-fa/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 3c50b07..b0e6e6e 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"فضای خصوصی"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"همسانهسازی"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"همگانی"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"فضای خصوصی"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"محتوای اعلان حساس پنهان شده است"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"بهدلایل امنیتی، محتوای برنامه از دید همرسانی صفحهنمایش پنهان شد"</string>
diff --git a/core/res/res/values-fi-rFI/donottranslate-cldr.xml b/core/res/res/values-fi-rFI/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-fi/donottranslate-cldr.xml b/core/res/res/values-fi/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 0e159f2..7e2fa9c 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Yksityinen tila"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klooni"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Yhteinen"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Yksityinen tila"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Arkaluontoisen ilmoituksen sisältö piilotettu"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Sovelluksen sisältö piilotettu näytön jakamiselta turvallisuussyistä"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 7fca5e6..a21aefb 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -2408,8 +2408,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espace privé"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Commun"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espace privé"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Le contenu confidentiel de la notification est masqué"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Le contenu de l\'application est masqué du Partage d\'écran par mesure de sécurité"</string>
diff --git a/core/res/res/values-fr/donottranslate-cldr.xml b/core/res/res/values-fr/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index e08f426..486124e 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -2408,8 +2408,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espace privé"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Commun"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espace privé"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Le contenu sensible de la notification a été masqué"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Le contenu de l\'appli est masqué lors du partage d\'écran pour des raisons de sécurité"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 1eec828..9994a3e 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espazo privado"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clonado"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Compartido"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espazo privado"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Contido confidencial da notificación oculto"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Por motivos de seguranza, ocultouse o contido da aplicación para que no se mostre na pantalla compartida"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 303f001..3206242 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"ખાનગી સ્પેસ"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ક્લોન"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"કૉમ્યુનલ"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ખાનગી સ્પેસ"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"સંવેદનશીલ માહિતીવાળા નોટિફિકેશનનું કન્ટેન્ટ છુપાવ્યું"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"સુરક્ષા માટે સ્ક્રીન શેર કરતી વખતે ઍપનું કન્ટેન્ટ છુપાવેલું છે"</string>
diff --git a/core/res/res/values-hi-rIN/donottranslate-cldr.xml b/core/res/res/values-hi-rIN/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-hi/donottranslate-cldr.xml b/core/res/res/values-hi/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 14d2bf7..f7ae13e 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"प्राइवेट स्पेस"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"क्लोन"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"कम्यूनिटी"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"प्राइवेट स्पेस"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"संवेदनशील जानकारी वाली सूचना का कॉन्टेंट छिपा है"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"स्क्रीन शेयर करने के दौरान सुरक्षा के लिए, ऐप्लिकेशन का कॉन्टेंट छिपाया गया"</string>
diff --git a/core/res/res/values-hr-rHR/donottranslate-cldr.xml b/core/res/res/values-hr-rHR/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-hr/donottranslate-cldr.xml b/core/res/res/values-hr/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index e69a59d..f6f7e8b 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -2408,8 +2408,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privatni prostor"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Zajedničko"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privatni prostor"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Skriven je osjetljiv sadržaj obavijesti"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Sadržaj aplikacije sakriven je od dijeljenja zaslona radi sigurnosti"</string>
diff --git a/core/res/res/values-hu-rHU/donottranslate-cldr.xml b/core/res/res/values-hu-rHU/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-hu/donottranslate-cldr.xml b/core/res/res/values-hu/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index f1ff236..5162616 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privát terület"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klón"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Közös"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privát terület"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Bizalmas értesítéstartalom elrejtve"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"A biztonság érdekében a képernyőmegosztástól elrejtett alkalmazástartalom"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 40812068..f2b6932 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Մասնավոր տարածք"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Կլոն"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Ընդհանուր"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Մասնավոր տարածք"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Ծանուցման զգայուն բովանդակությունը թաքցված է"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Անվտանգության նկատառումներից ելնելով՝ հավելվածի բովանդակությունը թաքցվել է էկրանի ցուցադրումից"</string>
diff --git a/core/res/res/values-in-rID/donottranslate-cldr.xml b/core/res/res/values-in-rID/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-in/donottranslate-cldr.xml b/core/res/res/values-in/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 866e946..d1a20f34 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Ruang privasi"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Umum"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Ruang privasi"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Konten notifikasi sensitif disembunyikan"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Konten aplikasi disembunyikan dari berbagi layar untuk alasan keamanan"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 76ba64b..c8da948 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Leynirými"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Afrit"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Sameiginlegt"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Leynirými"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Viðkvæmt tilkynningaefni falið"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Efni forrits falið í skjádeilingu af öryggisástæðum"</string>
diff --git a/core/res/res/values-it/donottranslate-cldr.xml b/core/res/res/values-it/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 5e87dce..403c522 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -2408,8 +2408,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Spazio privato"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Condiviso"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Spazio privato"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Contenuti sensibili della notifica nascosti"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Contenuti dell\'app nascosti dalla condivisione schermo per sicurezza"</string>
diff --git a/core/res/res/values-iw/donottranslate-cldr.xml b/core/res/res/values-iw/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 439565d..21e9293 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -2039,7 +2039,7 @@
<string name="pin_specific_target" msgid="7824671240625957415">"הצמדה של <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="unpin_target" msgid="3963318576590204447">"ביטול הצמדה"</string>
<string name="unpin_specific_target" msgid="3859828252160908146">"ביטול ההצמדה של <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="app_info" msgid="6113278084877079851">"פרטי אפליקציה"</string>
+ <string name="app_info" msgid="6113278084877079851">"פרטי האפליקציה"</string>
<string name="negative_duration" msgid="1938335096972945232">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="demo_starting_message" msgid="6577581216125805905">"תהליך ההדגמה מתחיל…"</string>
<string name="demo_restarting_message" msgid="1160053183701746766">"מתבצע איפוס של המכשיר…"</string>
@@ -2408,8 +2408,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"המרחב הפרטי"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"שכפול"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"שיתופי"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"המרחב הפרטי"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"יש תוכן רגיש בהתראה שהוסתר"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"תוכן האפליקציה מוסתר משיתוף המסך מטעמי אבטחה"</string>
diff --git a/core/res/res/values-ja/donottranslate-cldr.xml b/core/res/res/values-ja/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index fcaef68..149f3cd 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"プライベート スペース"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"複製"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"共用"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"プライベート スペース"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"プライベートな通知内容は表示されません"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"セキュリティ上、画面共有ではアプリの内容は非表示となります"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 2b98c58..ee1e89d 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"კერძო სივრცე"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"კლონის შექმნა"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"საერთო"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"კერძო სივრცე"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"სენსიტიური შეტყობინების კონტენტი დამალულია"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ეკრანის გაზიარებიდან აპის კონტენტი დამალულია უსაფრთხოების მიზნით"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 6e9eb12..63457e0 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Құпия кеңістік"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клон"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Жалпы"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Құпия кеңістік"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Хабарландырудың құпия контенті жасырылған."</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Қауіпсіздік мақсатында қолданба контенті экранды көрсету кезінде жасырылды."</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 70f59c1..7294d4b 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"លំហឯកជន"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ក្លូន"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"ទូទៅ"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"លំហឯកជន"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"បានលាក់ខ្លឹមសារជូនដំណឹងដែលមានលក្ខណៈរសើប"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"បានលាក់ខ្លឹមសារកម្មវិធីពីការបង្ហាញអេក្រង់ដើម្បីសុវត្ថិភាព"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index f22c43a..3d7c782 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"ಪ್ರೈವೆಟ್ ಸ್ಪೇಸ್"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ಕ್ಲೋನ್"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"ಸಮುದಾಯ"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ಪ್ರೈವೆಟ್ ಸ್ಪೇಸ್"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"ಸೂಕ್ಷ್ಮ ನೋಟಿಫಿಕೇಶನ್ ಕಂಟೆಂಟ್ ಅನ್ನು ಮರೆಮಾಡಲಾಗಿದೆ"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ಭದ್ರತೆಗಾಗಿ ಸ್ಕ್ರೀನ್ ಹಂಚಿಕೊಳ್ಳುವಿಕೆಯಲ್ಲಿ ಆ್ಯಪ್ ಕಂಟೆಂಟ್ ಅನ್ನು ಮರೆಮಾಡಲಾಗಿದೆ"</string>
diff --git a/core/res/res/values-ko/donottranslate-cldr.xml b/core/res/res/values-ko/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 99b8715..d815fe8 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"비공개 스페이스"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"클론"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"공동"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"비공개 스페이스"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"민감한 알림 콘텐츠 숨김"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"보안을 위해 화면 공유에서 앱 콘텐츠가 숨겨집니다."</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 427010b..3cdf3ca 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Жеке мейкиндик"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клон"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Жалпы"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Жеке мейкиндик"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Купуя билдирменин мазмуну жашырылган"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Коопсуздук үчүн колдонмодогу контент бөлүшүлгөн экрандан жашырылды"</string>
diff --git a/core/res/res/values-land/dimens.xml b/core/res/res/values-land/dimens.xml
index f58c4b0..c0916bc 100644
--- a/core/res/res/values-land/dimens.xml
+++ b/core/res/res/values-land/dimens.xml
@@ -69,7 +69,7 @@
<dimen name="timepicker_left_side_width">250dip</dimen>
<!-- width of ImmersiveModeConfirmation (-1 for match_parent) -->
- <dimen name="immersive_mode_cling_width">380dp</dimen>
+ <dimen name="immersive_mode_cling_width">500dp</dimen>
<!-- Floating toolbar dimensions -->
<dimen name="floating_toolbar_preferred_width">544dp</dimen>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 4682bb0..80e09a6 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"ພື້ນທີ່ສ່ວນບຸກຄົນ"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ໂຄລນ"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"ສ່ວນກາງ"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ພື້ນທີ່ສ່ວນບຸກຄົນ"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"ເນື້ອຫາການແຈ້ງເຕືອນທີ່ລະອຽດອ່ອນເຊື່ອງຢູ່"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ເນື້ອຫາແອັບຖືກເຊື່ອງໄວ້ຈາກການແບ່ງປັນໜ້າຈໍເພື່ອຄວາມປອດໄພ"</string>
diff --git a/core/res/res/values-lt-rLT/donottranslate-cldr.xml b/core/res/res/values-lt-rLT/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-lt/donottranslate-cldr.xml b/core/res/res/values-lt/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 6fbb9b8..b237241 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -2409,8 +2409,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privati erdvė"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klonuoti"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Bendruomenės"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privati erdvė"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Neskelbtinos informacijos pranešimo turinys paslėptas"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Programos turinys paslėptas bendrinant ekraną saugumo sumetimais"</string>
diff --git a/core/res/res/values-lv-rLV/donottranslate-cldr.xml b/core/res/res/values-lv-rLV/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-lv/donottranslate-cldr.xml b/core/res/res/values-lv/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 0bea4ff..cc902cd 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -2408,8 +2408,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privātā telpa"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klons"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Kopīgs"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privātā telpa"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Sensitīvs paziņojuma saturs ir paslēpts"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Drošības nolūkos lietotnes saturs kopīgotajā ekrānā ir paslēpts"</string>
diff --git a/core/res/res/values-mcc204-mnc04/config.xml b/core/res/res/values-mcc204-mnc04/config.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-mcc310-mnc004/config.xml b/core/res/res/values-mcc310-mnc004/config.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-mcc311-mnc480/config.xml b/core/res/res/values-mcc311-mnc480/config.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index d7a6dd2..b61b817 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Приватен простор"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клониран профил"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Профил на заедницата"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Приватен простор"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Содржината на чувствителните известувања е скриена"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Од безбедносни причини, содржините на апликацијата се скриени од споделувањето екран"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 4023336..9906470 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"സ്വകാര്യ സ്പേസ്"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ക്ലോൺ ചെയ്യുക"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"കമ്മ്യൂണൽ"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"സ്വകാര്യ സ്പേസ്"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"സൂക്ഷ്മമായി കൈകാര്യം ചെയ്യേണ്ട അറിയിപ്പ് ഉള്ളടക്കം മറച്ചിരിക്കുന്നു"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ആപ്പ് ഉള്ളടക്കം, അതിന്റെ സുരക്ഷയ്ക്കായി സ്ക്രീൻ പങ്കിടലിൽ നിന്ന് മറച്ചിരിക്കുന്നു"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 9aa18e2..22f8f15 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Хаалттай орон зай"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клон"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Нийтийн"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Хаалттай орон зай"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Эмзэг мэдэгдлийн контентыг нуусан"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Аюулгүй байдлын улмаас аппын контентыг дэлгэц хуваалцахаас нуусан"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index a115e2c..36d0bba 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"खाजगी स्पेस"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"क्लोन"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"सामुदायिक"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"खाजगी स्पेस"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"संवेदनशील नोटिफिकेशनचा आशय लपवलेला आहे"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"स्क्रीन शेअर करताना सुरक्षेसाठी अॅपमधील आशय लपवला आहे"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 2fa571c..c215f62 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Ruang persendirian"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Umum"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Ruang privasi"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Kandungan pemberitahuan yang sensitif disembunyikan"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Kandungan apl disembunyikan daripada perkongsian skrin untuk keselamatan"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 364a7b1..f030ef2 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"သီးသန့်နေရာ"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ပုံတူပွားရန်"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"အများသုံး"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"သီးသန့်နေရာ"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"သတိထားရမည့် အကြောင်းကြားချက်ပါ အချက်အလက်ကို ဖျောက်ထားသည်"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"အက်ပ်အကြောင်းအရာသည် လုံခြုံရေးအတွက် မျက်နှာပြင် မျှဝေခြင်းမှ ဖျောက်ထားသည်"</string>
diff --git a/core/res/res/values-nb/donottranslate-cldr.xml b/core/res/res/values-nb/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index e2de428..116c586 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privat område"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Felles"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privat område"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Sensitivt varselinnhold er skjult"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Av sikkerhetsgrunner er appinnholdet skjult for skjermdelingen"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 8d5f560..a202d4c 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"निजी स्पेस"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"क्लोन"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"सामुदायिक"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"निजी स्पेस"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"संवेदनशील सूचनासम्बन्धी सामग्री लुकाइएको छ"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"स्क्रिन सेयर गर्दा सुरक्षाका लागि एपमा भएको सामग्री लुकाइएको छ"</string>
diff --git a/core/res/res/values-nl/donottranslate-cldr.xml b/core/res/res/values-nl/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 5bc6c44..51d8959 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privégedeelte"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Kloon"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Gemeenschappelijk"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privégedeelte"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Content van gevoelige meldingen verborgen"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App-content verborgen voor scherm delen vanwege beveiligingsrisico\'s"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 4c7868d..90ab620 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"ପ୍ରାଇଭେଟ ସ୍ପେସ"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"କ୍ଲୋନ"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"କମ୍ୟୁନାଲ"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ପ୍ରାଇଭେଟ ସ୍ପେସ"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"ସମ୍ୱେଦନଶୀଳ ବିଜ୍ଞପ୍ତି ବିଷୟବସ୍ତୁକୁ ଲୁଚାଯାଇଛି"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ସୁରକ୍ଷା ପାଇଁ ସ୍କ୍ରିନ ସେୟାରରୁ ଆପ ବିଷୟବସ୍ତୁକୁ ଲୁଚାଯାଇଛି"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 38780b6..de91ea7 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ਕਲੋਨ"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"ਭਾਈਚਾਰਕ"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"ਲੁਕੀ ਹੋਈ ਸੰਵੇਦਨਸ਼ੀਲ ਸੂਚਨਾ ਸਮੱਗਰੀ"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ਐਪ ਸਮੱਗਰੀ ਨੂੰ ਸੁਰੱਖਿਆ ਲਈ ਸਕ੍ਰੀਨ ਸਾਂਝਾਕਰਨ ਤੋਂ ਲੁਕਾਇਆ ਗਿਆ ਹੈ"</string>
diff --git a/core/res/res/values-pl/donottranslate-cldr.xml b/core/res/res/values-pl/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 95e1a30..d9a95b8 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -2409,8 +2409,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Przestrzeń prywatna"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Wspólny"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Przestrzeń prywatna"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Treść poufnego powiadomienia została ukryta"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Ze względów bezpieczeństwa zawartość aplikacji jest niewidoczna podczas udostępniania ekranu"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index c9d36b1..cd3a7c5 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -2408,8 +2408,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espaço privado"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Público"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espaço privado"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Conteúdo de notificação sensível oculto"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Conteúdo do app oculto no compartilhamento de tela por motivos de segurança"</string>
diff --git a/core/res/res/values-pt-rPT/donottranslate-cldr.xml b/core/res/res/values-pt-rPT/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index f1b2bd0..d8fe4fe 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -733,7 +733,7 @@
<skip />
<string name="face_acquired_recalibrate_alt" msgid="5702674220280332115">"Não é possível criar o seu modelo de rosto. Tente novamente."</string>
<string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"Óculos escuros detetados. O seu rosto tem de estar completamente visível."</string>
- <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Máscara detetada. Todo o rosto tem de estar visível"</string>
+ <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Cobertura facial detetada. O seu rosto tem de estar completamente visível."</string>
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"Não pode validar o rosto. Hardware não disponível."</string>
@@ -1292,7 +1292,7 @@
<string name="android_upgrading_complete" msgid="409800058018374746">"A concluir o arranque."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Premiu o botão ligar/desligar. Geralmente, esta ação desliga o ecrã.\n\nExperimente tocar levemente ao configurar a sua impressão digital."</string>
<string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Para terminar, desligue o ecrã"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Desligar"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Desativar"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continuar a validar a impressão digital?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Premiu o botão ligar/desligar. Geralmente, esta ação desliga o ecrã.\n\nExperimente tocar levemente para validar a sua impressão digital."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Desligar ecrã"</string>
@@ -1438,7 +1438,7 @@
<string name="alert_windows_notification_channel_name" msgid="3437528564303192620">"A app <xliff:g id="NAME">%s</xliff:g> sobrepõe-se a outras aplicações"</string>
<string name="alert_windows_notification_title" msgid="6331662751095228536">"O <xliff:g id="NAME">%s</xliff:g> sobrepõe-se a outras app"</string>
<string name="alert_windows_notification_message" msgid="6538171456970725333">"Se não quer que a app <xliff:g id="NAME">%s</xliff:g> utilize esta funcionalidade, toque para abrir as definições e desative-a."</string>
- <string name="alert_windows_notification_turn_off_action" msgid="7805857234839123780">"Desligar"</string>
+ <string name="alert_windows_notification_turn_off_action" msgid="7805857234839123780">"Desativar"</string>
<string name="ext_media_checking_notification_title" msgid="8299199995416510094">"A verificar o <xliff:g id="NAME">%s</xliff:g>…"</string>
<string name="ext_media_checking_notification_message" msgid="2231566971425375542">"A rever o conteúdo atual…"</string>
<string name="ext_media_checking_notification_message" product="tv" msgid="7986154434946021415">"A analisar o armazenamento de multimédia"</string>
@@ -2408,8 +2408,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espaço privado"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Comum"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espaço privado"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Conteúdo das notificações sensíveis ocultado"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Conteúdo da app ocultado da partilha de ecrã por motivos de segurança"</string>
diff --git a/core/res/res/values-pt/donottranslate-cldr.xml b/core/res/res/values-pt/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index c9d36b1..cd3a7c5 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -2408,8 +2408,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espaço privado"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Público"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espaço privado"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Conteúdo de notificação sensível oculto"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Conteúdo do app oculto no compartilhamento de tela por motivos de segurança"</string>
diff --git a/core/res/res/values-ro-rRO/donottranslate-cldr.xml b/core/res/res/values-ro-rRO/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-ro/donottranslate-cldr.xml b/core/res/res/values-ro/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index e14aec7..d43556d 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -2408,8 +2408,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Spațiu privat"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clonă"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Comun"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Spațiu privat"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Conținutul sensibil din notificări a fost ascuns"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Conținutul aplicației este ascuns de permiterea accesului la ecran din motive de securitate"</string>
diff --git a/core/res/res/values-ru/donottranslate-cldr.xml b/core/res/res/values-ru/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 79b0daa..16ddae8 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -2409,8 +2409,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Частное пространство"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клонированный"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Совместный"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Частное пространство"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Конфиденциальная информация в уведомлении скрыта"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Содержимое приложения исключено из демонстрации экрана в целях безопасности."</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 1e69e0b..4f6e756 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"රහසිගත අවකාශය"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ක්ලෝන කරන්න"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"වාර්ගික"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"රහසිගත අවකාශය"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"සංවේදී දැනුම්දීම් අන්තර්ගතය සැඟවී ඇත"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ආරක්ෂාව සඳහා යෙදුම් අන්තර්ගතය තිරය බෙදා ගැනීමෙන් සඟවා ඇත"</string>
diff --git a/core/res/res/values-sk-rSK/donottranslate-cldr.xml b/core/res/res/values-sk-rSK/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-sk/donottranslate-cldr.xml b/core/res/res/values-sk/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index c7ca9f4..aeea7b6 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -2040,7 +2040,7 @@
<string name="pin_specific_target" msgid="7824671240625957415">"Pripnúť <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="unpin_target" msgid="3963318576590204447">"Uvoľniť"</string>
<string name="unpin_specific_target" msgid="3859828252160908146">"Odopnúť <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="app_info" msgid="6113278084877079851">"Info o aplikácii"</string>
+ <string name="app_info" msgid="6113278084877079851">"Informácie o aplikácii"</string>
<string name="negative_duration" msgid="1938335096972945232">"-<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="demo_starting_message" msgid="6577581216125805905">"Spúšťa sa ukážka…"</string>
<string name="demo_restarting_message" msgid="1160053183701746766">"Resetuje sa zariadenie…"</string>
@@ -2409,8 +2409,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Súkromný priestor"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Spoločný"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Súkromný priestor"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Obsah citlivého upozornenia je skrytý"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Obsah aplikácie bol na účely zabezpečenia skrytý v zdieľaní obrazovky"</string>
diff --git a/core/res/res/values-sl-rSI/donottranslate-cldr.xml b/core/res/res/values-sl-rSI/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-sl/donottranslate-cldr.xml b/core/res/res/values-sl/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 4f23a54..d3ce57c 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -2409,8 +2409,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Zasebni prostor"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Skupno"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Zasebni prostor"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Občutljiva vsebina obvestila je bila skrita"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Pri deljenju zaslona je vsebina aplikacije skrita zaradi varnosti"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index c49402b..de02100 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Hapësira private"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"I përbashkët"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Hapësira private"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Përmbajtjet delikate të njoftimeve janë fshehur"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Përmbajtja e aplikacionit është fshehur nga ndarja e ekranit për arsye sigurie"</string>
diff --git a/core/res/res/values-sr-rRS/donottranslate-cldr.xml b/core/res/res/values-sr-rRS/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-sr/donottranslate-cldr.xml b/core/res/res/values-sr/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 2b3f75a..f200ac1 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -2408,8 +2408,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Приватан простор"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клонирано"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Заједничко"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Приватан простор"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Осетљив садржај обавештења је скривен"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Садржај апликације је скривен за дељење садржаја екрана због безбедности"</string>
diff --git a/core/res/res/values-sv/donottranslate-cldr.xml b/core/res/res/values-sv/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 2833381..589414d 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -2038,7 +2038,7 @@
<string name="pin_specific_target" msgid="7824671240625957415">"Fäst <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="unpin_target" msgid="3963318576590204447">"Lossa"</string>
<string name="unpin_specific_target" msgid="3859828252160908146">"Lossa <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="app_info" msgid="6113278084877079851">"Info om appen"</string>
+ <string name="app_info" msgid="6113278084877079851">"Appinformation"</string>
<string name="negative_duration" msgid="1938335096972945232">"-<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="demo_starting_message" msgid="6577581216125805905">"Demo startas …"</string>
<string name="demo_restarting_message" msgid="1160053183701746766">"Enheten återställs …"</string>
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privat område"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klona"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Allmän"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privat område"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Känsligt aviseringsinnehåll dolt"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Av säkerhetsskäl döljs appinnehållet vid skärmdelning"</string>
diff --git a/core/res/res/values-sw/donottranslate-cldr.xml b/core/res/res/values-sw/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index d3af99f..4b50cb7 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Sehemu ya faragha"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Nakala"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Wasifu wa pamoja"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Sehemu ya faragha"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Maudhui nyeti kwenye arifa yamefichwa"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Maudhui ya programu yamefichwa ili yasionekane kwenye skrini ya pamoja kwa sababu za kiusalama"</string>
diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml
index 4c70ea3..4aed94c 100644
--- a/core/res/res/values-sw600dp/dimens.xml
+++ b/core/res/res/values-sw600dp/dimens.xml
@@ -112,7 +112,7 @@
<dimen name="keyguard_muliuser_selector_margin">12dp</dimen>
<!-- width of ImmersiveModeConfirmation (-1 for match_parent) -->
- <dimen name="immersive_mode_cling_width">380dp</dimen>
+ <dimen name="immersive_mode_cling_width">600dp</dimen>
<dimen name="floating_toolbar_preferred_width">544dp</dimen>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 37abcef..6ad8f59 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"ரகசிய இடம்"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"குளோன்"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"பொது"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ரகசிய இடம்"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"உணர்வுபூர்வமான அறிவிப்பு உள்ளடக்கம் மறைக்கப்பட்டது"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"பாதுகாப்பிற்காக, திரைப் பகிர்வில் இருந்து ஆப்ஸ் உள்ளடக்கம் மறைக்கப்பட்டுள்ளது"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 2766248..91f9ef2 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"ప్రైవేట్ స్పేస్"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"క్లోన్"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"కమ్యూనల్"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ప్రైవేట్ స్పేస్"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"గోప్యమైన నోటిఫికేషన్ కంటెంట్ దాచబడింది"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"సెక్యూరిటీ కోసం స్క్రీన్ షేర్ నుండి యాప్ కంటెంట్ దాచబడింది"</string>
diff --git a/core/res/res/values-th-rTH/donottranslate-cldr.xml b/core/res/res/values-th-rTH/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-th/donottranslate-cldr.xml b/core/res/res/values-th/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 1f2a091..eb102fc 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"พื้นที่ส่วนตัว"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"โคลน"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"ส่วนกลาง"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"พื้นที่ส่วนตัว"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"เนื้อหาการแจ้งเตือนที่ละเอียดอ่อนซ่อนอยู่"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ซ่อนเนื้อหาแอปจากการแชร์หน้าจอเพื่อความปลอดภัย"</string>
diff --git a/core/res/res/values-tl/donottranslate-cldr.xml b/core/res/res/values-tl/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 7bce4b7..de499d34 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Pribadong space"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Communal"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Pribadong space"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Nakatago ang content ng sensitibong notification"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Nakatago ang content ng app mula sa pagbabahagi ng screen para sa seguridad"</string>
diff --git a/core/res/res/values-tr/donottranslate-cldr.xml b/core/res/res/values-tr/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index cbfb770..7dab236 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Özel alan"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Paylaşılan"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Özel alan"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Hassas bildirim içerikleri gizlendi"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Uygulama içerikleri, güvenlik nedeniyle ekran paylaşımında gizlendi"</string>
diff --git a/core/res/res/values-uk-rUA/donottranslate-cldr.xml b/core/res/res/values-uk-rUA/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-uk/donottranslate-cldr.xml b/core/res/res/values-uk/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 5266acd..22c7272 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -2409,8 +2409,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Приватний простір"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Копія профілю"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Спільний профіль"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Приватний простір"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Чутливий вміст сповіщення приховано"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"З міркувань безпеки вміст додатка приховано під час показу екрана"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 2fe52f8..0d878c9 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"پرائیویٹ اسپیس"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"کلون"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"کمیونل"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"پرائیویٹ اسپیس"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"حساس اطلاعی مواد چھپا ہوا ہے"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"سیکیورٹی کے مد نظر ایپ کا مواد اسکرین کے اشتراک سے چھپا ہوا ہے"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index d40f383..fc5d8ee 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Maxfiy makon"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Nusxalash"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Umumiy"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Maxfiy makon"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Bildirishnomadagi maxfiy axborot berkitildi"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Ekran namoyishida xavfsizlik maqsadida ilova kontenti berkitildi"</string>
diff --git a/core/res/res/values-vi-rVN/donottranslate-cldr.xml b/core/res/res/values-vi-rVN/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-vi/donottranslate-cldr.xml b/core/res/res/values-vi/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 1051cda..4619be3 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Không gian riêng tư"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Nhân bản"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Dùng chung"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Không gian riêng tư"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Đã ẩn nội dung thông báo nhạy cảm"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Nội dung ứng dụng bị ẩn khỏi tính năng chia sẻ màn hình vì lý do bảo mật"</string>
diff --git a/core/res/res/values-w204dp-round-watch/dimens_material.xml b/core/res/res/values-w204dp-round-watch/dimens_material.xml
new file mode 100644
index 0000000..c07d5c4
--- /dev/null
+++ b/core/res/res/values-w204dp-round-watch/dimens_material.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <dimen name="screen_percentage_05">10.2dp</dimen>
+ <dimen name="screen_percentage_10">20.4dp</dimen>
+ <dimen name="screen_percentage_15">30.6dp</dimen>
+</resources>
diff --git a/core/res/res/values-zh-rCN/donottranslate-cldr.xml b/core/res/res/values-zh-rCN/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index d16a353..a6c62dc 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -581,7 +581,7 @@
<string name="permlab_changeTetherState" msgid="9079611809931863861">"更改网络共享连接"</string>
<string name="permdesc_changeTetherState" msgid="3025129606422533085">"允许应用更改绑定网络连接的状态。"</string>
<string name="permlab_accessWifiState" msgid="5552488500317911052">"查看WLAN连接"</string>
- <string name="permdesc_accessWifiState" msgid="6913641669259483363">"允许该应用查看WLAN网络的相关信息,例如是否启用了WLAN以及连接的WLAN设备的名称。"</string>
+ <string name="permdesc_accessWifiState" msgid="6913641669259483363">"允许该应用查看 WLAN 网络的相关信息,例如是否启用了 WLAN 以及连接的 WLAN 设备的名称。"</string>
<string name="permlab_changeWifiState" msgid="7947824109713181554">"连接WLAN网络和断开连接"</string>
<string name="permdesc_changeWifiState" msgid="7170350070554505384">"允许该应用与WLAN接入点建立和断开连接,以及更改WLAN网络的设备配置。"</string>
<string name="permlab_changeWifiMulticastState" msgid="285626875870754696">"允许接收WLAN多播"</string>
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"私密空间"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"克隆"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"共用"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"私密空间"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"已隐藏敏感通知内容"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"为安全起见而在屏幕共享画面中处于隐藏状态的应用内容"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 672a638..f9b10a2 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"私人空間"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"複製"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"共用"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"私人空間"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"已隱藏敏感通知內容"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"為安全起見,應用程式內容已從分享螢幕畫面隱藏"</string>
diff --git a/core/res/res/values-zh-rTW/donottranslate-cldr.xml b/core/res/res/values-zh-rTW/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 8a224ed..4dce59a 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"私人空間"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"複製"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"共通"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"私人空間"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"系統已隱藏含有私密資訊的通知內容"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"為安全起見,分享螢幕畫面未顯示應用程式內容"</string>
diff --git a/core/res/res/values-zu/donottranslate-cldr.xml b/core/res/res/values-zu/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 27bd3c9..60cb31b 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -2407,8 +2407,7 @@
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Indawo engasese"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Yenza i-Clone"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Okomphakathi"</string>
- <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) -->
- <skip />
+ <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Indawo engasese"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Okuqukethwe kwesaziso esizwelayo kufihliwe"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
<string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Okuqukethwe kwe-app kufihliwe kusuka ekwabelaneni kwesikrini ngokuvikelwa"</string>
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index 575573c..df5cbb1 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -37,8 +37,7 @@
<item>@drawable/fastscroll_label_right_material</item>
<item>@drawable/fastscroll_thumb_material</item>
<item>@drawable/fastscroll_track_material</item>
- <item>@drawable/floating_popup_background_dark</item>
- <item>@drawable/floating_popup_background_light</item>
+ <item>@drawable/floating_popup_background</item>
<item>@drawable/ic_ab_back_material</item>
<item>@drawable/ic_ab_back_material_dark</item>
<item>@drawable/ic_ab_back_material_light</item>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index c74bc9b..9846b71 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -606,11 +606,9 @@
<!-- ============ -->
<eat-comment />
<attr name="floatingToolbarCloseDrawable" format="reference" />
- <attr name="floatingToolbarForegroundColor" format="reference|color" />
<attr name="floatingToolbarItemBackgroundBorderlessDrawable" format="reference" />
<attr name="floatingToolbarItemBackgroundDrawable" format="reference" />
<attr name="floatingToolbarOpenDrawable" format="reference" />
- <attr name="floatingToolbarPopupBackgroundDrawable" format="reference" />
<attr name="floatingToolbarDividerColor" format="reference" />
<!-- ============ -->
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 27b756d..f94c8ab 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -851,7 +851,12 @@
of the screen.
<p>This attribute is supported by the <a
href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a>
- element. -->
+ element.
+ <aside class="note"><b>Note:</b> Device manufacturers can configure devices to override
+ (ignore) this attribute to improve the layout of apps. See
+ <a href="{@docRoot}guide/practices/device-compatibility-mode">
+ Device compatibility mode</a>.
+ </aside> -->
<attr name="screenOrientation">
<!-- No preference specified: let the system decide the best
orientation. This will either be the orientation selected
@@ -1447,13 +1452,23 @@
no other apps in multi-window visible on screen (e.g. picture-in-picture) or on other
displays. Therefore, this flag cannot be used to assure an exclusive resource access.
- <p>NOTE: A task's root activity value is applied to all additional activities launched in
+ <p>A task's root activity value is applied to all additional activities launched in
the task. That is if the root activity of a task is resizeable then the system will treat
all other activities in the task as resizeable and will not if the root activity isn't
resizeable.
- <p>NOTE: The value of {@link android.R.attr#screenOrientation} is ignored for
- resizeable activities when in multi-window mode before Android 12. -->
+ <aside class="note"><b>Note:</b>
+ <ul>
+ <li>On Android 11 (API level 30) and lower, the value of
+ {@link android.R.attr#screenOrientation} is ignored for resizeable activities
+ in multi-window mode.
+ <li>Device manufacturers can configure devices to override (ignore) this attribute
+ to force apps to resize. The override does not affect the app's support for
+ multi-window mode. See
+ <a href="{@docRoot}guide/practices/device-compatibility-mode">
+ Device compatibility mode</a>.
+ </ul>
+ </aside> -->
<attr name="resizeableActivity" format="boolean" />
<!-- Indicates that the activity specifically supports the picture-in-picture form of
@@ -1470,27 +1485,37 @@
<!-- This value indicates the maximum aspect ratio the activity supports. If the app runs on a
device with a wider aspect ratio, the system automatically letterboxes the app, leaving
portions of the screen unused so the app can run at its specified maximum aspect ratio.
- <p>
- Maximum aspect ratio, expressed as (longer dimension / shorter dimension) in decimal
+ <p>Maximum aspect ratio, expressed as (longer dimension / shorter dimension) in decimal
form. For example, if the maximum aspect ratio is 7:3, set value to 2.33.
- <p>
- Value needs to be greater or equal to 1.0, otherwise it is ignored.
- <p>
- NOTE: This attribute is ignored if the activity has
- {@link android.R.attr#resizeableActivity} set to true. -->
+ <p>Value needs to be greater or equal to 1.0, otherwise it is ignored.
+ <aside class="note"><b>Note:</b>
+ <ul>
+ <li>This attribute is ignored if the activity has
+ {@link android.R.attr#resizeableActivity} set to {@code true}.
+ <li>Device manufacturers can configure devices to override (ignore) this attribute
+ to improve the layout of apps. See
+ <a href="{@docRoot}guide/practices/device-compatibility-mode">
+ Device compatibility mode</a>.
+ </ul>
+ </aside> -->
<attr name="maxAspectRatio" format="float" />
<!-- This value indicates the minimum aspect ratio the activity supports. If the app runs on a
device with a narrower aspect ratio, the system automatically letterboxes the app, leaving
portions of the screen unused so the app can run at its specified minimum aspect ratio.
- <p>
- Minimum aspect ratio, expressed as (longer dimension / shorter dimension) in decimal
- form. For example, if the minimum aspect ratio is 4:3, set value to 1.33.
- <p>
- Value needs to be greater or equal to 1.0, otherwise it is ignored.
- <p>
- NOTE: This attribute is ignored if the activity has
- {@link android.R.attr#resizeableActivity} set to true. -->
+ <p>Minimum aspect ratio, expressed as (longer dimension / shorter dimension) in decimal
+ form. For example, if the minimum aspect ratio is 4:3, set value to 1.33.
+ <p>Value needs to be greater or equal to 1.0, otherwise it is ignored.
+ <aside class="note"><b>Note:</b>
+ <ul>
+ <li>This attribute is ignored if the activity has
+ {@link android.R.attr#resizeableActivity} set to {@code true}.
+ <li>Device manufacturers can configure devices to override (ignore) this attribute
+ to improve the layout of apps. See
+ <a href="{@docRoot}guide/practices/device-compatibility-mode">
+ Device compatibility mode</a>.
+ </ul>
+ </aside> -->
<attr name="minAspectRatio" format="float" />
<!-- This value indicates how tasks rooted at this activity will behave in lockTask mode.
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4dfe000..f43351a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -7076,4 +7076,8 @@
<!-- Whether the system uses auto-suspend mode. -->
<bool name="config_useAutoSuspend">true</bool>
+
+ <!-- Whether to show GAIA education screen during account login of private space setup.
+ OEM/Partner can explicitly opt to disable the screen. -->
+ <bool name="config_enableGaiaEducationInPrivateSpace">true</bool>
</resources>
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index dcda5d8..fba95a5 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -409,4 +409,10 @@
<bool name="config_force_phone_globals_creation">false</bool>
<java-symbol type="bool" name="config_force_phone_globals_creation" />
+ <!-- Boolean indicating whether to enable persistent logging via DropBoxManager.
+ Used in persisting SOS/emergency related log messages.
+ -->
+ <bool name="config_dropboxmanager_persistent_logging_enabled">false</bool>
+ <java-symbol type="bool" name="config_dropboxmanager_persistent_logging_enabled" />
+
</resources>
diff --git a/core/res/res/values/donottranslate-cldr.xml b/core/res/res/values/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index b547a7a..b6e8383 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5125,7 +5125,7 @@
<string name="immersive_cling_title">Viewing full screen</string>
<!-- Cling help message description when hiding the navigation bar entering immersive mode [CHAR LIMIT=none] -->
- <string name="immersive_cling_description">To exit, swipe down from the top.</string>
+ <string name="immersive_cling_description">To exit, swipe down from the top of your screen</string>
<!-- Cling help message confirmation button when hiding the navigation bar entering immersive mode [CHAR LIMIT=30] -->
<string name="immersive_cling_positive">Got it</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index cc74d02..c16bd24 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -521,6 +521,7 @@
<java-symbol type="bool" name="config_preferKeepClearForFocus" />
<java-symbol type="bool" name="config_hibernationDeletesOatArtifactsEnabled"/>
<java-symbol type="integer" name="config_defaultAnalogClockSecondsHandFps"/>
+ <java-symbol type="bool" name="config_enableGaiaEducationInPrivateSpace"/>
<java-symbol type="color" name="tab_indicator_text_v4" />
@@ -1618,6 +1619,7 @@
<java-symbol type="layout" name="restrictions_pin_challenge" />
<java-symbol type="layout" name="restrictions_pin_setup" />
<java-symbol type="layout" name="immersive_mode_cling" />
+ <java-symbol type="id" name="immersive_cling_icon" />
<java-symbol type="layout" name="user_switching_dialog" />
<java-symbol type="layout" name="common_tab_settings" />
<java-symbol type="layout" name="resolver_list_per_profile" />
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index bdbf96b..c3d304d 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -391,11 +391,9 @@
<!-- Floating toolbar styles -->
<item name="floatingToolbarCloseDrawable">@drawable/ic_ab_back_material_dark</item>
- <item name="floatingToolbarForegroundColor">@color/foreground_material_dark</item>
<item name="floatingToolbarItemBackgroundBorderlessDrawable">@drawable/item_background_borderless_material_dark</item>
<item name="floatingToolbarItemBackgroundDrawable">@drawable/item_background_material_dark</item>
<item name="floatingToolbarOpenDrawable">@drawable/ic_menu_moreoverflow_material_dark</item>
- <item name="floatingToolbarPopupBackgroundDrawable">@drawable/floating_popup_background_dark</item>
<item name="floatingToolbarDividerColor">@color/floating_popup_divider_dark</item>
<!-- SearchView attributes -->
@@ -579,11 +577,9 @@
<!-- Floating toolbar styles -->
<item name="floatingToolbarCloseDrawable">@drawable/ic_ab_back_material_light</item>
- <item name="floatingToolbarForegroundColor">@color/foreground_material_light</item>
<item name="floatingToolbarItemBackgroundBorderlessDrawable">@drawable/item_background_borderless_material_light</item>
<item name="floatingToolbarItemBackgroundDrawable">@drawable/item_background_material_light</item>
<item name="floatingToolbarOpenDrawable">@drawable/ic_menu_moreoverflow_material_light</item>
- <item name="floatingToolbarPopupBackgroundDrawable">@drawable/floating_popup_background_light</item>
<item name="floatingToolbarDividerColor">@color/floating_popup_divider_light</item>
<!-- Tooltip popup colors -->
diff --git a/core/sysprop/Android.bp b/core/sysprop/Android.bp
index 512a2eb..ed82765 100644
--- a/core/sysprop/Android.bp
+++ b/core/sysprop/Android.bp
@@ -43,3 +43,10 @@
property_owner: "Platform",
api_packages: ["android.sysprop"],
}
+
+sysprop_library {
+ name: "com.android.sysprop.view",
+ srcs: ["ViewProperties.sysprop"],
+ property_owner: "Platform",
+ api_packages: ["android.sysprop"],
+}
diff --git a/core/sysprop/ViewProperties.sysprop b/core/sysprop/ViewProperties.sysprop
new file mode 100644
index 0000000..e801643
--- /dev/null
+++ b/core/sysprop/ViewProperties.sysprop
@@ -0,0 +1,29 @@
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module: "android.sysprop.ViewProperties"
+owner: Platform
+
+# On low-end devices, the cost of calculating frame rate can
+# have noticeable overhead. These devices don't benefit from
+# reduced frame rate as much as they benefit from reduced
+# work. By setting this to false, the device won't do any
+# VRR frame rate calculation for Views.
+prop {
+ api_name: "vrr_enabled"
+ type: Boolean
+ prop_name: "ro.view.vrr.enabled"
+ scope: Internal
+ access: Readonly
+}
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java
index 4f9b269..4c3d4e3 100644
--- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java
@@ -16,8 +16,6 @@
package android.hardware.radio;
-import static com.google.common.truth.Truth.assertWithMessage;
-
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -36,6 +34,8 @@
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.ArrayMap;
+import com.google.common.truth.Expect;
+
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -155,6 +155,9 @@
private RadioManager mRadioManager;
private final ApplicationInfo mApplicationInfo = new ApplicationInfo();
+ @Rule
+ public final Expect mExpect = Expect.create();
+
@Mock
private IRadioService mRadioServiceMock;
@Mock
@@ -175,7 +178,7 @@
() -> new RadioManager.AmBandDescriptor(REGION, /* type= */ 100, AM_LOWER_LIMIT,
AM_UPPER_LIMIT, AM_SPACING, STEREO_SUPPORTED));
- assertWithMessage("Unsupported band type exception")
+ mExpect.withMessage("Unsupported band type exception")
.that(thrown).hasMessageThat().contains("Unsupported band");
}
@@ -183,7 +186,7 @@
public void getType_forBandDescriptor() {
RadioManager.BandDescriptor bandDescriptor = createAmBandDescriptor();
- assertWithMessage("AM Band Descriptor type")
+ mExpect.withMessage("AM Band Descriptor type")
.that(bandDescriptor.getType()).isEqualTo(RadioManager.BAND_AM);
}
@@ -191,7 +194,7 @@
public void getRegion_forBandDescriptor() {
RadioManager.BandDescriptor bandDescriptor = createFmBandDescriptor();
- assertWithMessage("FM Band Descriptor region")
+ mExpect.withMessage("FM Band Descriptor region")
.that(bandDescriptor.getRegion()).isEqualTo(REGION);
}
@@ -199,7 +202,7 @@
public void getLowerLimit_forBandDescriptor() {
RadioManager.BandDescriptor bandDescriptor = createFmBandDescriptor();
- assertWithMessage("FM Band Descriptor lower limit")
+ mExpect.withMessage("FM Band Descriptor lower limit")
.that(bandDescriptor.getLowerLimit()).isEqualTo(FM_LOWER_LIMIT);
}
@@ -207,7 +210,7 @@
public void getUpperLimit_forBandDescriptor() {
RadioManager.BandDescriptor bandDescriptor = createAmBandDescriptor();
- assertWithMessage("AM Band Descriptor upper limit")
+ mExpect.withMessage("AM Band Descriptor upper limit")
.that(bandDescriptor.getUpperLimit()).isEqualTo(AM_UPPER_LIMIT);
}
@@ -215,7 +218,7 @@
public void getSpacing_forBandDescriptor() {
RadioManager.BandDescriptor bandDescriptor = createAmBandDescriptor();
- assertWithMessage("AM Band Descriptor spacing")
+ mExpect.withMessage("AM Band Descriptor spacing")
.that(bandDescriptor.getSpacing()).isEqualTo(AM_SPACING);
}
@@ -223,7 +226,7 @@
public void describeContents_forBandDescriptor() {
RadioManager.BandDescriptor bandDescriptor = createFmBandDescriptor();
- assertWithMessage("Band Descriptor contents")
+ mExpect.withMessage("Band Descriptor contents")
.that(bandDescriptor.describeContents()).isEqualTo(0);
}
@@ -237,7 +240,7 @@
RadioManager.BandDescriptor bandDescriptorFromParcel =
RadioManager.BandDescriptor.CREATOR.createFromParcel(parcel);
- assertWithMessage("Band Descriptor created from parcel")
+ mExpect.withMessage("Band Descriptor created from parcel")
.that(bandDescriptorFromParcel).isEqualTo(bandDescriptor);
}
@@ -246,14 +249,14 @@
RadioManager.BandDescriptor[] bandDescriptors =
RadioManager.BandDescriptor.CREATOR.newArray(CREATOR_ARRAY_SIZE);
- assertWithMessage("Band Descriptors").that(bandDescriptors).hasLength(CREATOR_ARRAY_SIZE);
+ mExpect.withMessage("Band Descriptors").that(bandDescriptors).hasLength(CREATOR_ARRAY_SIZE);
}
@Test
public void isAmBand_forAmBandDescriptor_returnsTrue() {
RadioManager.BandDescriptor bandDescriptor = createAmBandDescriptor();
- assertWithMessage("Is AM Band Descriptor an AM band")
+ mExpect.withMessage("Is AM Band Descriptor an AM band")
.that(bandDescriptor.isAmBand()).isTrue();
}
@@ -261,43 +264,43 @@
public void isFmBand_forAmBandDescriptor_returnsFalse() {
RadioManager.BandDescriptor bandDescriptor = createAmBandDescriptor();
- assertWithMessage("Is AM Band Descriptor an FM band")
+ mExpect.withMessage("Is AM Band Descriptor an FM band")
.that(bandDescriptor.isFmBand()).isFalse();
}
@Test
public void isStereoSupported_forFmBandDescriptor() {
- assertWithMessage("FM Band Descriptor stereo")
+ mExpect.withMessage("FM Band Descriptor stereo")
.that(FM_BAND_DESCRIPTOR.isStereoSupported()).isEqualTo(STEREO_SUPPORTED);
}
@Test
public void isRdsSupported_forFmBandDescriptor() {
- assertWithMessage("FM Band Descriptor RDS or RBDS")
+ mExpect.withMessage("FM Band Descriptor RDS or RBDS")
.that(FM_BAND_DESCRIPTOR.isRdsSupported()).isEqualTo(RDS_SUPPORTED);
}
@Test
public void isTaSupported_forFmBandDescriptor() {
- assertWithMessage("FM Band Descriptor traffic announcement")
+ mExpect.withMessage("FM Band Descriptor traffic announcement")
.that(FM_BAND_DESCRIPTOR.isTaSupported()).isEqualTo(TA_SUPPORTED);
}
@Test
public void isAfSupported_forFmBandDescriptor() {
- assertWithMessage("FM Band Descriptor alternate frequency")
+ mExpect.withMessage("FM Band Descriptor alternate frequency")
.that(FM_BAND_DESCRIPTOR.isAfSupported()).isEqualTo(AF_SUPPORTED);
}
@Test
public void isEaSupported_forFmBandDescriptor() {
- assertWithMessage("FM Band Descriptor emergency announcement")
+ mExpect.withMessage("FM Band Descriptor emergency announcement")
.that(FM_BAND_DESCRIPTOR.isEaSupported()).isEqualTo(EA_SUPPORTED);
}
@Test
public void describeContents_forFmBandDescriptor() {
- assertWithMessage("FM Band Descriptor contents")
+ mExpect.withMessage("FM Band Descriptor contents")
.that(FM_BAND_DESCRIPTOR.describeContents()).isEqualTo(0);
}
@@ -310,7 +313,7 @@
RadioManager.FmBandDescriptor fmBandDescriptorFromParcel =
RadioManager.FmBandDescriptor.CREATOR.createFromParcel(parcel);
- assertWithMessage("FM Band Descriptor created from parcel")
+ mExpect.withMessage("FM Band Descriptor created from parcel")
.that(fmBandDescriptorFromParcel).isEqualTo(FM_BAND_DESCRIPTOR);
}
@@ -319,19 +322,19 @@
RadioManager.FmBandDescriptor[] fmBandDescriptors =
RadioManager.FmBandDescriptor.CREATOR.newArray(CREATOR_ARRAY_SIZE);
- assertWithMessage("FM Band Descriptors")
+ mExpect.withMessage("FM Band Descriptors")
.that(fmBandDescriptors).hasLength(CREATOR_ARRAY_SIZE);
}
@Test
public void isStereoSupported_forAmBandDescriptor() {
- assertWithMessage("AM Band Descriptor stereo")
+ mExpect.withMessage("AM Band Descriptor stereo")
.that(AM_BAND_DESCRIPTOR.isStereoSupported()).isEqualTo(STEREO_SUPPORTED);
}
@Test
public void describeContents_forAmBandDescriptor() {
- assertWithMessage("AM Band Descriptor contents")
+ mExpect.withMessage("AM Band Descriptor contents")
.that(AM_BAND_DESCRIPTOR.describeContents()).isEqualTo(0);
}
@@ -344,7 +347,7 @@
RadioManager.AmBandDescriptor amBandDescriptorFromParcel =
RadioManager.AmBandDescriptor.CREATOR.createFromParcel(parcel);
- assertWithMessage("FM Band Descriptor created from parcel")
+ mExpect.withMessage("FM Band Descriptor created from parcel")
.that(amBandDescriptorFromParcel).isEqualTo(AM_BAND_DESCRIPTOR);
}
@@ -353,7 +356,7 @@
RadioManager.AmBandDescriptor[] amBandDescriptors =
RadioManager.AmBandDescriptor.CREATOR.newArray(CREATOR_ARRAY_SIZE);
- assertWithMessage("AM Band Descriptors")
+ mExpect.withMessage("AM Band Descriptors")
.that(amBandDescriptors).hasLength(CREATOR_ARRAY_SIZE);
}
@@ -361,7 +364,7 @@
public void equals_withSameFmBandDescriptors_returnsTrue() {
RadioManager.FmBandDescriptor fmBandDescriptorCompared = createFmBandDescriptor();
- assertWithMessage("The same FM Band Descriptor")
+ mExpect.withMessage("The same FM Band Descriptor")
.that(FM_BAND_DESCRIPTOR).isEqualTo(fmBandDescriptorCompared);
}
@@ -369,19 +372,19 @@
public void equals_withSameAmBandDescriptors_returnsTrue() {
RadioManager.AmBandDescriptor amBandDescriptorCompared = createAmBandDescriptor();
- assertWithMessage("The same AM Band Descriptor")
+ mExpect.withMessage("The same AM Band Descriptor")
.that(AM_BAND_DESCRIPTOR).isEqualTo(amBandDescriptorCompared);
}
@Test
public void equals_withAmBandDescriptorsAndOtherTypeObject() {
- assertWithMessage("AM Band Descriptor")
+ mExpect.withMessage("AM Band Descriptor")
.that(AM_BAND_DESCRIPTOR).isNotEqualTo(FM_BAND_DESCRIPTOR);
}
@Test
public void equals_withFmBandDescriptorsAndOtherTypeObject() {
- assertWithMessage("FM Band Descriptor")
+ mExpect.withMessage("FM Band Descriptor")
.that(FM_BAND_DESCRIPTOR).isNotEqualTo(AM_BAND_DESCRIPTOR);
}
@@ -391,7 +394,7 @@
new RadioManager.AmBandDescriptor(REGION, RadioManager.BAND_AM, AM_LOWER_LIMIT,
AM_UPPER_LIMIT + AM_SPACING, AM_SPACING, STEREO_SUPPORTED);
- assertWithMessage("AM Band Descriptor of different upper limit")
+ mExpect.withMessage("AM Band Descriptor of different upper limit")
.that(AM_BAND_DESCRIPTOR).isNotEqualTo(amBandDescriptorCompared);
}
@@ -401,7 +404,7 @@
new RadioManager.AmBandDescriptor(REGION, RadioManager.BAND_AM, AM_LOWER_LIMIT,
AM_UPPER_LIMIT, AM_SPACING, !STEREO_SUPPORTED);
- assertWithMessage("AM Band Descriptor of different stereo support values")
+ mExpect.withMessage("AM Band Descriptor of different stereo support values")
.that(AM_BAND_DESCRIPTOR).isNotEqualTo(amBandDescriptorCompared);
}
@@ -411,7 +414,7 @@
REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING * 2,
STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED);
- assertWithMessage("FM Band Descriptors of different support limit values")
+ mExpect.withMessage("FM Band Descriptors of different support limit values")
.that(FM_BAND_DESCRIPTOR).isNotEqualTo(fmBandDescriptorCompared);
}
@@ -421,7 +424,7 @@
REGION + 1, RadioManager.BAND_AM_HD, AM_LOWER_LIMIT, AM_UPPER_LIMIT, AM_SPACING,
STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED);
- assertWithMessage("FM Band Descriptors of different region values")
+ mExpect.withMessage("FM Band Descriptors of different region values")
.that(FM_BAND_DESCRIPTOR).isNotEqualTo(fmBandDescriptorCompared);
}
@@ -431,7 +434,7 @@
REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING,
!STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED);
- assertWithMessage("FM Band Descriptors of different stereo support values")
+ mExpect.withMessage("FM Band Descriptors of different stereo support values")
.that(fmBandDescriptorCompared).isNotEqualTo(FM_BAND_DESCRIPTOR);
}
@@ -441,7 +444,7 @@
REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING,
STEREO_SUPPORTED, !RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED);
- assertWithMessage("FM Band Descriptors of different rds support values")
+ mExpect.withMessage("FM Band Descriptors of different rds support values")
.that(fmBandDescriptorCompared).isNotEqualTo(FM_BAND_DESCRIPTOR);
}
@@ -451,7 +454,7 @@
REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING,
STEREO_SUPPORTED, RDS_SUPPORTED, !TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED);
- assertWithMessage("FM Band Descriptors of different ta support values")
+ mExpect.withMessage("FM Band Descriptors of different ta support values")
.that(fmBandDescriptorCompared).isNotEqualTo(FM_BAND_DESCRIPTOR);
}
@@ -461,7 +464,7 @@
REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING,
STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, !AF_SUPPORTED, EA_SUPPORTED);
- assertWithMessage("FM Band Descriptors of different af support values")
+ mExpect.withMessage("FM Band Descriptors of different af support values")
.that(fmBandDescriptorCompared).isNotEqualTo(FM_BAND_DESCRIPTOR);
}
@@ -471,7 +474,7 @@
REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING,
STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, !EA_SUPPORTED);
- assertWithMessage("FM Band Descriptors of different ea support values")
+ mExpect.withMessage("FM Band Descriptors of different ea support values")
.that(fmBandDescriptorCompared).isNotEqualTo(FM_BAND_DESCRIPTOR);
}
@@ -479,7 +482,7 @@
public void hashCode_withSameFmBandDescriptors_equals() {
RadioManager.FmBandDescriptor fmBandDescriptorCompared = createFmBandDescriptor();
- assertWithMessage("Hash code of the same FM Band Descriptor")
+ mExpect.withMessage("Hash code of the same FM Band Descriptor")
.that(fmBandDescriptorCompared.hashCode()).isEqualTo(FM_BAND_DESCRIPTOR.hashCode());
}
@@ -487,7 +490,7 @@
public void hashCode_withSameAmBandDescriptors_equals() {
RadioManager.AmBandDescriptor amBandDescriptorCompared = createAmBandDescriptor();
- assertWithMessage("Hash code of the same AM Band Descriptor")
+ mExpect.withMessage("Hash code of the same AM Band Descriptor")
.that(amBandDescriptorCompared.hashCode()).isEqualTo(AM_BAND_DESCRIPTOR.hashCode());
}
@@ -497,7 +500,7 @@
REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING,
STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, !AF_SUPPORTED, EA_SUPPORTED);
- assertWithMessage("Hash code of FM Band Descriptor of different spacing")
+ mExpect.withMessage("Hash code of FM Band Descriptor of different spacing")
.that(fmBandDescriptorCompared.hashCode())
.isNotEqualTo(FM_BAND_DESCRIPTOR.hashCode());
}
@@ -508,7 +511,7 @@
new RadioManager.AmBandDescriptor(REGION, RadioManager.BAND_AM, AM_LOWER_LIMIT,
AM_UPPER_LIMIT, AM_SPACING * 2, STEREO_SUPPORTED);
- assertWithMessage("Hash code of AM Band Descriptor of different spacing")
+ mExpect.withMessage("Hash code of AM Band Descriptor of different spacing")
.that(amBandDescriptorCompared.hashCode())
.isNotEqualTo(AM_BAND_DESCRIPTOR.hashCode());
}
@@ -517,7 +520,7 @@
public void getType_forBandConfig() {
RadioManager.BandConfig fmBandConfig = createFmBandConfig();
- assertWithMessage("FM Band Config type")
+ mExpect.withMessage("FM Band Config type")
.that(fmBandConfig.getType()).isEqualTo(RadioManager.BAND_FM);
}
@@ -525,7 +528,7 @@
public void getRegion_forBandConfig() {
RadioManager.BandConfig amBandConfig = createAmBandConfig();
- assertWithMessage("AM Band Config region")
+ mExpect.withMessage("AM Band Config region")
.that(amBandConfig.getRegion()).isEqualTo(REGION);
}
@@ -533,7 +536,7 @@
public void getLowerLimit_forBandConfig() {
RadioManager.BandConfig amBandConfig = createAmBandConfig();
- assertWithMessage("AM Band Config lower limit")
+ mExpect.withMessage("AM Band Config lower limit")
.that(amBandConfig.getLowerLimit()).isEqualTo(AM_LOWER_LIMIT);
}
@@ -541,7 +544,7 @@
public void getUpperLimit_forBandConfig() {
RadioManager.BandConfig fmBandConfig = createFmBandConfig();
- assertWithMessage("FM Band Config upper limit")
+ mExpect.withMessage("FM Band Config upper limit")
.that(fmBandConfig.getUpperLimit()).isEqualTo(FM_UPPER_LIMIT);
}
@@ -549,7 +552,7 @@
public void getSpacing_forBandConfig() {
RadioManager.BandConfig fmBandConfig = createFmBandConfig();
- assertWithMessage("FM Band Config spacing")
+ mExpect.withMessage("FM Band Config spacing")
.that(fmBandConfig.getSpacing()).isEqualTo(FM_SPACING);
}
@@ -557,7 +560,7 @@
public void describeContents_forBandConfig() {
RadioManager.BandConfig bandConfig = createFmBandConfig();
- assertWithMessage("FM Band Config contents")
+ mExpect.withMessage("FM Band Config contents")
.that(bandConfig.describeContents()).isEqualTo(0);
}
@@ -571,7 +574,7 @@
RadioManager.BandConfig bandConfigFromParcel =
RadioManager.BandConfig.CREATOR.createFromParcel(parcel);
- assertWithMessage("Band Config created from parcel")
+ mExpect.withMessage("Band Config created from parcel")
.that(bandConfigFromParcel).isEqualTo(bandConfig);
}
@@ -580,42 +583,42 @@
RadioManager.BandConfig[] bandConfigs =
RadioManager.BandConfig.CREATOR.newArray(CREATOR_ARRAY_SIZE);
- assertWithMessage("Band Configs").that(bandConfigs).hasLength(CREATOR_ARRAY_SIZE);
+ mExpect.withMessage("Band Configs").that(bandConfigs).hasLength(CREATOR_ARRAY_SIZE);
}
@Test
public void getStereo_forFmBandConfig() {
- assertWithMessage("FM Band Config stereo")
+ mExpect.withMessage("FM Band Config stereo")
.that(FM_BAND_CONFIG.getStereo()).isEqualTo(STEREO_SUPPORTED);
}
@Test
public void getRds_forFmBandConfig() {
- assertWithMessage("FM Band Config RDS or RBDS")
+ mExpect.withMessage("FM Band Config RDS or RBDS")
.that(FM_BAND_CONFIG.getRds()).isEqualTo(RDS_SUPPORTED);
}
@Test
public void getTa_forFmBandConfig() {
- assertWithMessage("FM Band Config traffic announcement")
+ mExpect.withMessage("FM Band Config traffic announcement")
.that(FM_BAND_CONFIG.getTa()).isEqualTo(TA_SUPPORTED);
}
@Test
public void getAf_forFmBandConfig() {
- assertWithMessage("FM Band Config alternate frequency")
+ mExpect.withMessage("FM Band Config alternate frequency")
.that(FM_BAND_CONFIG.getAf()).isEqualTo(AF_SUPPORTED);
}
@Test
public void getEa_forFmBandConfig() {
- assertWithMessage("FM Band Config emergency Announcement")
+ mExpect.withMessage("FM Band Config emergency Announcement")
.that(FM_BAND_CONFIG.getEa()).isEqualTo(EA_SUPPORTED);
}
@Test
public void describeContents_forFmBandConfig() {
- assertWithMessage("FM Band Config contents")
+ mExpect.withMessage("FM Band Config contents")
.that(FM_BAND_CONFIG.describeContents()).isEqualTo(0);
}
@@ -628,7 +631,7 @@
RadioManager.FmBandConfig fmBandConfigFromParcel =
RadioManager.FmBandConfig.CREATOR.createFromParcel(parcel);
- assertWithMessage("FM Band Config created from parcel")
+ mExpect.withMessage("FM Band Config created from parcel")
.that(fmBandConfigFromParcel).isEqualTo(FM_BAND_CONFIG);
}
@@ -637,18 +640,18 @@
RadioManager.FmBandConfig[] fmBandConfigs =
RadioManager.FmBandConfig.CREATOR.newArray(CREATOR_ARRAY_SIZE);
- assertWithMessage("FM Band Configs").that(fmBandConfigs).hasLength(CREATOR_ARRAY_SIZE);
+ mExpect.withMessage("FM Band Configs").that(fmBandConfigs).hasLength(CREATOR_ARRAY_SIZE);
}
@Test
public void getStereo_forAmBandConfig() {
- assertWithMessage("AM Band Config stereo")
+ mExpect.withMessage("AM Band Config stereo")
.that(AM_BAND_CONFIG.getStereo()).isEqualTo(STEREO_SUPPORTED);
}
@Test
public void describeContents_forAmBandConfig() {
- assertWithMessage("AM Band Config contents")
+ mExpect.withMessage("AM Band Config contents")
.that(AM_BAND_CONFIG.describeContents()).isEqualTo(0);
}
@@ -661,7 +664,7 @@
RadioManager.AmBandConfig amBandConfigFromParcel =
RadioManager.AmBandConfig.CREATOR.createFromParcel(parcel);
- assertWithMessage("AM Band Config created from parcel")
+ mExpect.withMessage("AM Band Config created from parcel")
.that(amBandConfigFromParcel).isEqualTo(AM_BAND_CONFIG);
}
@@ -670,7 +673,7 @@
RadioManager.AmBandConfig[] amBandConfigs =
RadioManager.AmBandConfig.CREATOR.newArray(CREATOR_ARRAY_SIZE);
- assertWithMessage("AM Band Configs").that(amBandConfigs).hasLength(CREATOR_ARRAY_SIZE);
+ mExpect.withMessage("AM Band Configs").that(amBandConfigs).hasLength(CREATOR_ARRAY_SIZE);
}
@Test
@@ -679,7 +682,7 @@
new RadioManager.FmBandConfig.Builder(FM_BAND_CONFIG);
RadioManager.FmBandConfig fmBandConfigCompared = builder.build();
- assertWithMessage("The same FM Band Config")
+ mExpect.withMessage("The same FM Band Config")
.that(FM_BAND_CONFIG).isEqualTo(fmBandConfigCompared);
}
@@ -690,7 +693,7 @@
AM_LOWER_LIMIT, AM_UPPER_LIMIT, AM_SPACING, STEREO_SUPPORTED, RDS_SUPPORTED,
TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED));
- assertWithMessage("FM Band Config of different regions")
+ mExpect.withMessage("FM Band Config of different regions")
.that(FM_BAND_CONFIG).isNotEqualTo(fmBandConfigCompared);
}
@@ -701,7 +704,7 @@
FM_UPPER_LIMIT, FM_SPACING, !STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED,
AF_SUPPORTED, EA_SUPPORTED));
- assertWithMessage("FM Band Config with different stereo support values")
+ mExpect.withMessage("FM Band Config with different stereo support values")
.that(fmBandConfigCompared).isNotEqualTo(FM_BAND_CONFIG);
}
@@ -712,7 +715,7 @@
FM_UPPER_LIMIT, FM_SPACING, STEREO_SUPPORTED, !RDS_SUPPORTED, TA_SUPPORTED,
AF_SUPPORTED, EA_SUPPORTED));
- assertWithMessage("FM Band Config with different RDS support values")
+ mExpect.withMessage("FM Band Config with different RDS support values")
.that(fmBandConfigCompared).isNotEqualTo(FM_BAND_CONFIG);
}
@@ -723,7 +726,7 @@
FM_UPPER_LIMIT, FM_SPACING, STEREO_SUPPORTED, RDS_SUPPORTED, !TA_SUPPORTED,
AF_SUPPORTED, EA_SUPPORTED));
- assertWithMessage("FM Band Configs with different ta values")
+ mExpect.withMessage("FM Band Configs with different ta values")
.that(fmBandConfigCompared).isNotEqualTo(FM_BAND_CONFIG);
}
@@ -734,7 +737,7 @@
.setTa(TA_SUPPORTED).setAf(!AF_SUPPORTED).setEa(EA_SUPPORTED);
RadioManager.FmBandConfig fmBandConfigCompared = builder.build();
- assertWithMessage("FM Band Config of different af support value")
+ mExpect.withMessage("FM Band Config of different af support value")
.that(FM_BAND_CONFIG).isNotEqualTo(fmBandConfigCompared);
}
@@ -745,19 +748,19 @@
FM_UPPER_LIMIT, FM_SPACING, STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED,
AF_SUPPORTED, !EA_SUPPORTED));
- assertWithMessage("FM Band Configs with different ea support values")
+ mExpect.withMessage("FM Band Configs with different ea support values")
.that(fmBandConfigCompared).isNotEqualTo(FM_BAND_CONFIG);
}
@Test
public void equals_withAmBandConfigsAndOtherTypeObject() {
- assertWithMessage("AM Band Config")
+ mExpect.withMessage("AM Band Config")
.that(AM_BAND_CONFIG).isNotEqualTo(FM_BAND_CONFIG);
}
@Test
public void equals_withFmBandConfigsAndOtherTypeObject() {
- assertWithMessage("FM Band Config")
+ mExpect.withMessage("FM Band Config")
.that(FM_BAND_CONFIG).isNotEqualTo(AM_BAND_CONFIG);
}
@@ -767,7 +770,7 @@
new RadioManager.AmBandConfig.Builder(AM_BAND_CONFIG);
RadioManager.AmBandConfig amBandConfigCompared = builder.build();
- assertWithMessage("The same AM Band Config")
+ mExpect.withMessage("The same AM Band Config")
.that(AM_BAND_CONFIG).isEqualTo(amBandConfigCompared);
}
@@ -777,7 +780,7 @@
new RadioManager.AmBandDescriptor(REGION, RadioManager.BAND_AM_HD, AM_LOWER_LIMIT,
AM_UPPER_LIMIT, AM_SPACING, STEREO_SUPPORTED));
- assertWithMessage("AM Band Config of different type")
+ mExpect.withMessage("AM Band Config of different type")
.that(AM_BAND_CONFIG).isNotEqualTo(amBandConfigCompared);
}
@@ -787,7 +790,7 @@
createAmBandDescriptor()).setStereo(!STEREO_SUPPORTED);
RadioManager.AmBandConfig amBandConfigFromBuilder = builder.build();
- assertWithMessage("AM Band Config of different stereo value")
+ mExpect.withMessage("AM Band Config of different stereo value")
.that(AM_BAND_CONFIG).isNotEqualTo(amBandConfigFromBuilder);
}
@@ -795,7 +798,7 @@
public void hashCode_withSameFmBandConfigs_equals() {
RadioManager.FmBandConfig fmBandConfigCompared = createFmBandConfig();
- assertWithMessage("Hash code of the same FM Band Config")
+ mExpect.withMessage("Hash code of the same FM Band Config")
.that(FM_BAND_CONFIG.hashCode()).isEqualTo(fmBandConfigCompared.hashCode());
}
@@ -803,7 +806,7 @@
public void hashCode_withSameAmBandConfigs_equals() {
RadioManager.AmBandConfig amBandConfigCompared = createAmBandConfig();
- assertWithMessage("Hash code of the same AM Band Config")
+ mExpect.withMessage("Hash code of the same AM Band Config")
.that(amBandConfigCompared.hashCode()).isEqualTo(AM_BAND_CONFIG.hashCode());
}
@@ -814,7 +817,7 @@
FM_UPPER_LIMIT, FM_SPACING, STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED,
AF_SUPPORTED, EA_SUPPORTED));
- assertWithMessage("Hash code of FM Band Config with different type")
+ mExpect.withMessage("Hash code of FM Band Config with different type")
.that(fmBandConfigCompared.hashCode()).isNotEqualTo(FM_BAND_CONFIG.hashCode());
}
@@ -824,87 +827,87 @@
new RadioManager.AmBandDescriptor(REGION, RadioManager.BAND_AM, AM_LOWER_LIMIT,
AM_UPPER_LIMIT, AM_SPACING, !STEREO_SUPPORTED));
- assertWithMessage("Hash code of AM Band Config with different stereo support")
+ mExpect.withMessage("Hash code of AM Band Config with different stereo support")
.that(amBandConfigCompared.hashCode()).isNotEqualTo(AM_BAND_CONFIG.hashCode());
}
@Test
public void getId_forModuleProperties() {
- assertWithMessage("Properties id")
+ mExpect.withMessage("Properties id")
.that(AMFM_PROPERTIES.getId()).isEqualTo(PROPERTIES_ID);
}
@Test
public void getServiceName_forModuleProperties() {
- assertWithMessage("Properties service name")
+ mExpect.withMessage("Properties service name")
.that(AMFM_PROPERTIES.getServiceName()).isEqualTo(SERVICE_NAME);
}
@Test
public void getClassId_forModuleProperties() {
- assertWithMessage("Properties class ID")
+ mExpect.withMessage("Properties class ID")
.that(AMFM_PROPERTIES.getClassId()).isEqualTo(CLASS_ID);
}
@Test
public void getImplementor_forModuleProperties() {
- assertWithMessage("Properties implementor")
+ mExpect.withMessage("Properties implementor")
.that(AMFM_PROPERTIES.getImplementor()).isEqualTo(IMPLEMENTOR);
}
@Test
public void getProduct_forModuleProperties() {
- assertWithMessage("Properties product")
+ mExpect.withMessage("Properties product")
.that(AMFM_PROPERTIES.getProduct()).isEqualTo(PRODUCT);
}
@Test
public void getVersion_forModuleProperties() {
- assertWithMessage("Properties version")
+ mExpect.withMessage("Properties version")
.that(AMFM_PROPERTIES.getVersion()).isEqualTo(VERSION);
}
@Test
public void getSerial_forModuleProperties() {
- assertWithMessage("Serial properties")
+ mExpect.withMessage("Serial properties")
.that(AMFM_PROPERTIES.getSerial()).isEqualTo(SERIAL);
}
@Test
public void getNumTuners_forModuleProperties() {
- assertWithMessage("Number of tuners in properties")
+ mExpect.withMessage("Number of tuners in properties")
.that(AMFM_PROPERTIES.getNumTuners()).isEqualTo(NUM_TUNERS);
}
@Test
public void getNumAudioSources_forModuleProperties() {
- assertWithMessage("Number of audio sources in properties")
+ mExpect.withMessage("Number of audio sources in properties")
.that(AMFM_PROPERTIES.getNumAudioSources()).isEqualTo(NUM_AUDIO_SOURCES);
}
@Test
public void isInitializationRequired_forModuleProperties() {
- assertWithMessage("Initialization required in properties")
+ mExpect.withMessage("Initialization required in properties")
.that(AMFM_PROPERTIES.isInitializationRequired())
.isEqualTo(IS_INITIALIZATION_REQUIRED);
}
@Test
public void isCaptureSupported_forModuleProperties() {
- assertWithMessage("Capture support in properties")
+ mExpect.withMessage("Capture support in properties")
.that(AMFM_PROPERTIES.isCaptureSupported()).isEqualTo(IS_CAPTURE_SUPPORTED);
}
@Test
public void isBackgroundScanningSupported_forModuleProperties() {
- assertWithMessage("Background scan support in properties")
+ mExpect.withMessage("Background scan support in properties")
.that(AMFM_PROPERTIES.isBackgroundScanningSupported())
.isEqualTo(IS_BG_SCAN_SUPPORTED);
}
@Test
public void isProgramTypeSupported_withSupportedType_forModuleProperties() {
- assertWithMessage("AM/FM frequency type radio support in properties")
+ mExpect.withMessage("AM/FM frequency type radio support in properties")
.that(AMFM_PROPERTIES.isProgramTypeSupported(
ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY))
.isTrue();
@@ -912,28 +915,28 @@
@Test
public void isProgramTypeSupported_withNonSupportedType_forModuleProperties() {
- assertWithMessage("DAB frequency type radio support in properties")
+ mExpect.withMessage("DAB frequency type radio support in properties")
.that(AMFM_PROPERTIES.isProgramTypeSupported(
ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY)).isFalse();
}
@Test
public void isProgramIdentifierSupported_withSupportedIdentifier_forModuleProperties() {
- assertWithMessage("AM/FM frequency identifier radio support in properties")
+ mExpect.withMessage("AM/FM frequency identifier radio support in properties")
.that(AMFM_PROPERTIES.isProgramIdentifierSupported(
ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY)).isTrue();
}
@Test
public void isProgramIdentifierSupported_withNonSupportedIdentifier_forModuleProperties() {
- assertWithMessage("DAB frequency identifier radio support in properties")
+ mExpect.withMessage("DAB frequency identifier radio support in properties")
.that(AMFM_PROPERTIES.isProgramIdentifierSupported(
ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY)).isFalse();
}
@Test
public void getDabFrequencyTable_forModulePropertiesInitializedWithNullTable() {
- assertWithMessage("Properties DAB frequency table")
+ mExpect.withMessage("Properties DAB frequency table")
.that(AMFM_PROPERTIES.getDabFrequencyTable()).isNull();
}
@@ -941,32 +944,32 @@
public void getDabFrequencyTable_forModulePropertiesInitializedWithEmptyTable() {
RadioManager.ModuleProperties properties = createAmFmProperties(new ArrayMap<>());
- assertWithMessage("Properties DAB frequency table")
+ mExpect.withMessage("Properties DAB frequency table")
.that(properties.getDabFrequencyTable()).isNull();
}
@Test
public void getVendorInfo_forModuleProperties() {
- assertWithMessage("Properties vendor info")
+ mExpect.withMessage("Properties vendor info")
.that(AMFM_PROPERTIES.getVendorInfo()).isEmpty();
}
@Test
public void getBands_forModuleProperties() {
- assertWithMessage("Properties bands")
+ mExpect.withMessage("Properties bands")
.that(AMFM_PROPERTIES.getBands()).asList()
.containsExactly(AM_BAND_DESCRIPTOR, FM_BAND_DESCRIPTOR);
}
@Test
public void describeContents_forModuleProperties() {
- assertWithMessage("Module properties contents")
+ mExpect.withMessage("Module properties contents")
.that(AMFM_PROPERTIES.describeContents()).isEqualTo(0);
}
@Test
public void toString_forModuleProperties() {
- assertWithMessage("Module properties string").that(AMFM_PROPERTIES.toString())
+ mExpect.withMessage("Module properties string").that(AMFM_PROPERTIES.toString())
.contains(AM_BAND_DESCRIPTOR.toString() + ", " + FM_BAND_DESCRIPTOR.toString());
}
@@ -979,7 +982,7 @@
RadioManager.ModuleProperties modulePropertiesFromParcel =
RadioManager.ModuleProperties.CREATOR.createFromParcel(parcel);
- assertWithMessage("Module properties created from parcel")
+ mExpect.withMessage("Module properties created from parcel")
.that(modulePropertiesFromParcel).isEqualTo(AMFM_PROPERTIES);
}
@@ -994,7 +997,7 @@
RadioManager.ModuleProperties modulePropertiesFromParcel =
RadioManager.ModuleProperties.CREATOR.createFromParcel(parcel);
- assertWithMessage("Module properties created from parcel")
+ mExpect.withMessage("Module properties created from parcel")
.that(modulePropertiesFromParcel).isEqualTo(propertiesToParcel);
}
@@ -1003,7 +1006,7 @@
RadioManager.ModuleProperties propertiesCompared =
createAmFmProperties(/* dabFrequencyTable= */ null);
- assertWithMessage("The same module properties")
+ mExpect.withMessage("The same module properties")
.that(AMFM_PROPERTIES).isEqualTo(propertiesCompared);
}
@@ -1016,7 +1019,7 @@
SUPPORTED_PROGRAM_TYPES, SUPPORTED_IDENTIFIERS_TYPES, Map.of("5A", 174928),
/* vendorInfo= */ null);
- assertWithMessage("Module properties of different id")
+ mExpect.withMessage("Module properties of different id")
.that(AMFM_PROPERTIES).isNotEqualTo(propertiesDab);
}
@@ -1025,7 +1028,7 @@
RadioManager.ModuleProperties propertiesCompared =
createAmFmProperties(/* dabFrequencyTable= */ null);
- assertWithMessage("Hash code of the same module properties")
+ mExpect.withMessage("Hash code of the same module properties")
.that(propertiesCompared.hashCode()).isEqualTo(AMFM_PROPERTIES.hashCode());
}
@@ -1034,86 +1037,86 @@
RadioManager.ModuleProperties[] modulePropertiesArray =
RadioManager.ModuleProperties.CREATOR.newArray(CREATOR_ARRAY_SIZE);
- assertWithMessage("Module properties array")
+ mExpect.withMessage("Module properties array")
.that(modulePropertiesArray).hasLength(CREATOR_ARRAY_SIZE);
}
@Test
public void getSelector_forProgramInfo() {
- assertWithMessage("Selector of DAB program info")
+ mExpect.withMessage("Selector of DAB program info")
.that(DAB_PROGRAM_INFO.getSelector()).isEqualTo(DAB_SELECTOR);
}
@Test
public void getLogicallyTunedTo_forProgramInfo() {
- assertWithMessage("Identifier logically tuned to in DAB program info")
+ mExpect.withMessage("Identifier logically tuned to in DAB program info")
.that(DAB_PROGRAM_INFO.getLogicallyTunedTo()).isEqualTo(DAB_SID_EXT_IDENTIFIER);
}
@Test
public void getPhysicallyTunedTo_forProgramInfo() {
- assertWithMessage("Identifier physically tuned to DAB program info")
+ mExpect.withMessage("Identifier physically tuned to DAB program info")
.that(DAB_PROGRAM_INFO.getPhysicallyTunedTo()).isEqualTo(DAB_FREQUENCY_IDENTIFIER);
}
@Test
public void getRelatedContent_forProgramInfo() {
- assertWithMessage("DAB program info contents")
+ mExpect.withMessage("DAB program info contents")
.that(DAB_PROGRAM_INFO.getRelatedContent())
.containsExactly(DAB_SID_EXT_IDENTIFIER_RELATED);
}
@Test
public void getChannel_forProgramInfo() {
- assertWithMessage("Main channel of DAB program info")
+ mExpect.withMessage("Main channel of DAB program info")
.that(DAB_PROGRAM_INFO.getChannel()).isEqualTo(0);
}
@Test
public void getSubChannel_forProgramInfo() {
- assertWithMessage("Sub channel of DAB program info")
+ mExpect.withMessage("Sub channel of DAB program info")
.that(DAB_PROGRAM_INFO.getSubChannel()).isEqualTo(0);
}
@Test
public void isTuned_forProgramInfo() {
- assertWithMessage("Tuned status of DAB program info")
+ mExpect.withMessage("Tuned status of DAB program info")
.that(DAB_PROGRAM_INFO.isTuned()).isTrue();
}
@Test
public void isStereo_forProgramInfo() {
- assertWithMessage("Stereo support in DAB program info")
+ mExpect.withMessage("Stereo support in DAB program info")
.that(DAB_PROGRAM_INFO.isStereo()).isTrue();
}
@Test
public void isDigital_forProgramInfo() {
- assertWithMessage("Digital DAB program info")
+ mExpect.withMessage("Digital DAB program info")
.that(DAB_PROGRAM_INFO.isDigital()).isTrue();
}
@Test
public void isLive_forProgramInfo() {
- assertWithMessage("Live status of DAB program info")
+ mExpect.withMessage("Live status of DAB program info")
.that(DAB_PROGRAM_INFO.isLive()).isTrue();
}
@Test
public void isMuted_forProgramInfo() {
- assertWithMessage("Muted status of DAB program info")
+ mExpect.withMessage("Muted status of DAB program info")
.that(DAB_PROGRAM_INFO.isMuted()).isFalse();
}
@Test
public void isTrafficProgram_forProgramInfo() {
- assertWithMessage("Traffic program support in DAB program info")
+ mExpect.withMessage("Traffic program support in DAB program info")
.that(DAB_PROGRAM_INFO.isTrafficProgram()).isFalse();
}
@Test
public void isTrafficAnnouncementActive_forProgramInfo() {
- assertWithMessage("Active traffic announcement for DAB program info")
+ mExpect.withMessage("Active traffic announcement for DAB program info")
.that(DAB_PROGRAM_INFO.isTrafficAnnouncementActive()).isFalse();
}
@@ -1121,7 +1124,7 @@
public void isSignalAcquired_forProgramInfo() {
mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
- assertWithMessage("Signal acquisition status for HD program info")
+ mExpect.withMessage("Signal acquisition status for HD program info")
.that(HD_PROGRAM_INFO.isSignalAcquired()).isTrue();
}
@@ -1129,7 +1132,7 @@
public void isHdSisAvailable_forProgramInfo() {
mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
- assertWithMessage("SIS information acquisition status for HD program")
+ mExpect.withMessage("SIS information acquisition status for HD program")
.that(HD_PROGRAM_INFO.isHdSisAvailable()).isTrue();
}
@@ -1137,31 +1140,31 @@
public void isHdAudioAvailable_forProgramInfo() {
mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
- assertWithMessage("Audio acquisition status for HD program")
+ mExpect.withMessage("Audio acquisition status for HD program")
.that(HD_PROGRAM_INFO.isHdAudioAvailable()).isFalse();
}
@Test
public void getSignalStrength_forProgramInfo() {
- assertWithMessage("Signal strength of DAB program info")
+ mExpect.withMessage("Signal strength of DAB program info")
.that(DAB_PROGRAM_INFO.getSignalStrength()).isEqualTo(SIGNAL_QUALITY);
}
@Test
public void getMetadata_forProgramInfo() {
- assertWithMessage("Metadata of DAB program info")
+ mExpect.withMessage("Metadata of DAB program info")
.that(DAB_PROGRAM_INFO.getMetadata()).isEqualTo(METADATA);
}
@Test
public void getVendorInfo_forProgramInfo() {
- assertWithMessage("Vendor info of DAB program info")
+ mExpect.withMessage("Vendor info of DAB program info")
.that(DAB_PROGRAM_INFO.getVendorInfo()).isEmpty();
}
@Test
public void describeContents_forProgramInfo() {
- assertWithMessage("Program info contents")
+ mExpect.withMessage("Program info contents")
.that(DAB_PROGRAM_INFO.describeContents()).isEqualTo(0);
}
@@ -1170,7 +1173,7 @@
RadioManager.ProgramInfo[] programInfoArray =
RadioManager.ProgramInfo.CREATOR.newArray(CREATOR_ARRAY_SIZE);
- assertWithMessage("Program infos").that(programInfoArray).hasLength(CREATOR_ARRAY_SIZE);
+ mExpect.withMessage("Program infos").that(programInfoArray).hasLength(CREATOR_ARRAY_SIZE);
}
@Test
@@ -1182,7 +1185,7 @@
RadioManager.ProgramInfo programInfoFromParcel =
RadioManager.ProgramInfo.CREATOR.createFromParcel(parcel);
- assertWithMessage("Program info created from parcel")
+ mExpect.withMessage("Program info created from parcel")
.that(programInfoFromParcel).isEqualTo(DAB_PROGRAM_INFO);
}
@@ -1190,7 +1193,7 @@
public void equals_withSameProgramInfo_returnsTrue() {
RadioManager.ProgramInfo dabProgramInfoCompared = createDabProgramInfo(DAB_SELECTOR);
- assertWithMessage("The same program info")
+ mExpect.withMessage("The same program info")
.that(dabProgramInfoCompared).isEqualTo(DAB_PROGRAM_INFO);
}
@@ -1202,7 +1205,7 @@
/* vendorIds= */ null);
RadioManager.ProgramInfo dabProgramInfoCompared = createDabProgramInfo(dabSelectorCompared);
- assertWithMessage("Program info with different secondary id selectors")
+ mExpect.withMessage("Program info with different secondary id selectors")
.that(DAB_PROGRAM_INFO).isNotEqualTo(dabProgramInfoCompared);
}
@@ -1213,7 +1216,7 @@
mRadioManager.listModules(modules);
- assertWithMessage("Modules in radio manager")
+ mExpect.withMessage("Modules in radio manager")
.that(modules).containsExactly(AMFM_PROPERTIES);
}
@@ -1221,7 +1224,7 @@
public void listModules_forRadioManagerWithNullListAsInput_fails() throws Exception {
createRadioManager();
- assertWithMessage("Status when listing module with empty list input")
+ mExpect.withMessage("Status when listing module with empty list input")
.that(mRadioManager.listModules(null)).isEqualTo(RadioManager.STATUS_BAD_VALUE);
}
@@ -1231,7 +1234,7 @@
when(mRadioServiceMock.listModules()).thenReturn(null);
List<RadioManager.ModuleProperties> modules = new ArrayList<>();
- assertWithMessage("Status for listing module when getting null list from HAL client")
+ mExpect.withMessage("Status for listing module when getting null list from HAL client")
.that(mRadioManager.listModules(modules)).isEqualTo(RadioManager.STATUS_ERROR);
}
@@ -1241,7 +1244,7 @@
when(mRadioServiceMock.listModules()).thenThrow(new RemoteException());
List<RadioManager.ModuleProperties> modules = new ArrayList<>();
- assertWithMessage("Status for listing module when HAL client service is dead")
+ mExpect.withMessage("Status for listing module when HAL client service is dead")
.that(mRadioManager.listModules(modules))
.isEqualTo(RadioManager.STATUS_DEAD_OBJECT);
}
@@ -1267,7 +1270,21 @@
RadioTuner nullTuner = mRadioManager.openTuner(/* moduleId= */ 0, FM_BAND_CONFIG,
/* withAudio= */ true, mCallbackMock, /* handler= */ null);
- assertWithMessage("Radio tuner when service is dead").that(nullTuner).isNull();
+ mExpect.withMessage("Radio tuner when service is dead").that(nullTuner).isNull();
+ }
+
+ @Test
+ public void openTuner_withNullCallback() throws Exception {
+ createRadioManager();
+ int moduleId = 0;
+ boolean withAudio = true;
+
+ IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
+ () -> mRadioManager.openTuner(moduleId, FM_BAND_CONFIG, withAudio,
+ /* callback= */ null, /* handler= */ null));
+
+ mExpect.withMessage("Null tuner callback exception").that(thrown)
+ .hasMessageThat().contains("callback must not be empty");
}
@Test
@@ -1323,7 +1340,7 @@
RuntimeException thrown = assertThrows(RuntimeException.class,
() -> mRadioManager.addAnnouncementListener(enableTypeSet, mEventListener));
- assertWithMessage("Exception for adding announcement listener with dead service")
+ mExpect.withMessage("Exception for adding announcement listener with dead service")
.that(thrown).hasMessageThat().contains(exceptionMessage);
}
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AnnouncementAggregatorTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AnnouncementAggregatorTest.java
index 0e0dbec..2bf0aa3 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AnnouncementAggregatorTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AnnouncementAggregatorTest.java
@@ -16,11 +16,11 @@
package com.android.server.broadcastradio.aidl;
-import static com.google.common.truth.Truth.assertWithMessage;
-
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -33,7 +33,10 @@
import android.os.IBinder;
import android.os.RemoteException;
+import com.google.common.truth.Expect;
+
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -54,6 +57,9 @@
private AnnouncementAggregator mAnnouncementAggregator;
private IBinder.DeathRecipient mDeathRecipient;
+ @Rule
+ public final Expect mExpect = Expect.create();
+
@Mock
private IAnnouncementListener mListenerMock;
@Mock
@@ -75,6 +81,18 @@
}
@Test
+ public void constructor_withBinderDied() throws Exception {
+ RemoteException remoteException = new RemoteException("Binder is died");
+ doThrow(remoteException).when(mBinderMock).linkToDeath(any(), anyInt());
+
+ RuntimeException thrown = assertThrows(RuntimeException.class, () ->
+ new AnnouncementAggregator(mListenerMock, mLock));
+
+ mExpect.withMessage("Exception for dead binder").that(thrown).hasMessageThat()
+ .contains(remoteException.getMessage());
+ }
+
+ @Test
public void onListUpdated_withOneModuleWatcher() throws Exception {
ArgumentCaptor<IAnnouncementListener> moduleWatcherCaptor =
ArgumentCaptor.forClass(IAnnouncementListener.class);
@@ -103,7 +121,7 @@
moduleWatcherCaptor.getValue().onListUpdated(Arrays.asList(mAnnouncementMocks[index]));
verify(mListenerMock, times(index + 1)).onListUpdated(announcementsCaptor.capture());
- assertWithMessage("Number of announcements %s after %s announcements were updated",
+ mExpect.withMessage("Number of announcements %s after %s announcements were updated",
announcementsCaptor.getValue(), index + 1)
.that(announcementsCaptor.getValue().size()).isEqualTo(index + 1);
}
@@ -131,7 +149,7 @@
() -> mAnnouncementAggregator.watchModule(mRadioModuleMocks[0],
TEST_ENABLED_TYPES));
- assertWithMessage("Exception for watching module after aggregator has been closed")
+ mExpect.withMessage("Exception for watching module after aggregator has been closed")
.that(thrown).hasMessageThat()
.contains("announcement aggregator has already been closed");
}
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
index 8d9fad9..42501c1 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
@@ -27,6 +27,7 @@
import android.hardware.broadcastradio.DabTableEntry;
import android.hardware.broadcastradio.IdentifierType;
import android.hardware.broadcastradio.Metadata;
+import android.hardware.broadcastradio.ProgramFilter;
import android.hardware.broadcastradio.ProgramIdentifier;
import android.hardware.broadcastradio.ProgramInfo;
import android.hardware.broadcastradio.Properties;
@@ -41,6 +42,7 @@
import android.hardware.radio.UniqueProgramIdentifier;
import android.os.ServiceSpecificException;
import android.platform.test.flag.junit.SetFlagsRule;
+import android.util.ArraySet;
import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
import com.android.server.broadcastradio.ExtendedRadioMockitoTestCase;
@@ -93,6 +95,11 @@
private static final long TEST_HD_LOCATION_VALUE = 0x4E647007665CF6L;
private static final long TEST_VENDOR_ID_VALUE = 9_901;
+ private static final ProgramSelector.Identifier TEST_INVALID_ID =
+ new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_INVALID, 1);
+ private static final ProgramIdentifier TEST_HAL_INVALID_ID =
+ AidlTestUtils.makeHalIdentifier(IdentifierType.INVALID, 1);
+
private static final ProgramSelector.Identifier TEST_DAB_SID_EXT_ID =
new ProgramSelector.Identifier(
ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT, TEST_DAB_DMB_SID_EXT_VALUE);
@@ -139,7 +146,7 @@
private static final int TEST_ANNOUNCEMENT_FREQUENCY = FM_LOWER_LIMIT + FM_SPACING;
private static final RadioManager.ModuleProperties MODULE_PROPERTIES =
- convertToModuleProperties();
+ createModuleProperties();
private static final Announcement ANNOUNCEMENT =
ConversionUtils.announcementFromHalAnnouncement(
AidlTestUtils.makeAnnouncement(TEST_ENABLED_TYPE, TEST_ANNOUNCEMENT_FREQUENCY));
@@ -291,6 +298,37 @@
}
@Test
+ public void propertiesFromHalProperties_withoutAmFmAndDabConfigs() {
+ RadioManager.ModuleProperties properties = createModuleProperties(/* amFmConfig= */ null,
+ new DabTableEntry[]{});
+
+ expect.withMessage("Empty AM/FM config")
+ .that(properties.getBands()).asList().isEmpty();
+ expect.withMessage("Empty DAB config")
+ .that(properties.getDabFrequencyTable()).isNull();
+ }
+
+ @Test
+ public void propertiesFromHalProperties_withInvalidBand() {
+ AmFmRegionConfig amFmRegionConfig = new AmFmRegionConfig();
+ amFmRegionConfig.ranges = new AmFmBandRange[]{createAmFmBandRange(/* lowerBound= */ 50000,
+ /* upperBound= */ 60000, /* spacing= */ 10),
+ createAmFmBandRange(FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING)};
+
+ RadioManager.ModuleProperties properties = createModuleProperties(amFmRegionConfig,
+ new DabTableEntry[]{});
+
+ RadioManager.BandDescriptor[] bands = properties.getBands();
+ expect.withMessage("Band descriptors").that(bands).hasLength(1);
+ expect.withMessage("FM band frequency lower limit")
+ .that(bands[0].getLowerLimit()).isEqualTo(FM_LOWER_LIMIT);
+ expect.withMessage("FM band frequency upper limit")
+ .that(bands[0].getUpperLimit()).isEqualTo(FM_UPPER_LIMIT);
+ expect.withMessage("FM band frequency spacing")
+ .that(bands[0].getSpacing()).isEqualTo(FM_SPACING);
+ }
+
+ @Test
public void identifierToHalProgramIdentifier_withDabId() {
ProgramIdentifier halDabId =
ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_SID_EXT_ID);
@@ -358,6 +396,13 @@
}
@Test
+ public void identifierFromHalProgramIdentifier_withInvalidIdentifier() {
+ expect.withMessage("Identifier converted from invalid HAL identifier")
+ .that(ConversionUtils.identifierFromHalProgramIdentifier(TEST_HAL_INVALID_ID))
+ .isNull();
+ }
+
+ @Test
public void programSelectorToHalProgramSelector_withValidSelector() {
android.hardware.broadcastradio.ProgramSelector halDabSelector =
ConversionUtils.programSelectorToHalProgramSelector(TEST_DAB_SELECTOR);
@@ -370,6 +415,23 @@
}
@Test
+ public void programSelectorToHalProgramSelector_withInvalidSecondaryId() {
+ ProgramSelector dabSelector = new ProgramSelector(ProgramSelector.PROGRAM_TYPE_DAB,
+ TEST_DAB_SID_EXT_ID, new ProgramSelector.Identifier[]{TEST_INVALID_ID,
+ TEST_DAB_FREQUENCY_ID, TEST_DAB_ENSEMBLE_ID}, /* vendorIds= */ null);
+
+ android.hardware.broadcastradio.ProgramSelector halDabSelector =
+ ConversionUtils.programSelectorToHalProgramSelector(dabSelector);
+
+ expect.withMessage("Primary identifier of converted HAL DAB selector with invalid "
+ + "secondary id").that(halDabSelector.primaryId)
+ .isEqualTo(TEST_HAL_DAB_SID_EXT_ID);
+ expect.withMessage("Secondary identifiers of converted HAL DAB selector with "
+ + "invalid secondary id").that(halDabSelector.secondaryIds).asList()
+ .containsExactly(TEST_HAL_DAB_FREQUENCY_ID, TEST_HAL_DAB_ENSEMBLE_ID);
+ }
+
+ @Test
public void programSelectorFromHalProgramSelector_withValidSelector() {
android.hardware.broadcastradio.ProgramSelector halDabSelector =
AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID, new ProgramIdentifier[]{
@@ -386,6 +448,33 @@
}
@Test
+ public void programSelectorFromHalProgramSelector_withInvalidSelector() {
+ android.hardware.broadcastradio.ProgramSelector invalidSelector =
+ AidlTestUtils.makeHalSelector(TEST_HAL_INVALID_ID, new ProgramIdentifier[]{});
+
+ expect.withMessage("Selector converted from invalid HAL selector")
+ .that(ConversionUtils.programSelectorFromHalProgramSelector(invalidSelector))
+ .isNull();
+ }
+
+ @Test
+ public void programSelectorFromHalProgramSelector_withInvalidSecondaryId() {
+ android.hardware.broadcastradio.ProgramSelector halDabSelector =
+ AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID, new ProgramIdentifier[]{
+ TEST_HAL_INVALID_ID, TEST_HAL_DAB_ENSEMBLE_ID, TEST_HAL_DAB_FREQUENCY_ID});
+
+ ProgramSelector dabSelector =
+ ConversionUtils.programSelectorFromHalProgramSelector(halDabSelector);
+
+ expect.withMessage("Primary identifier of converted DAB selector with invalid "
+ + "secondary id").that(dabSelector.getPrimaryId())
+ .isEqualTo(TEST_DAB_SID_EXT_ID);
+ expect.withMessage("Secondary identifiers of converted DAB selector with invalid "
+ + "secondary id").that(dabSelector.getSecondaryIds()).asList()
+ .containsExactly(TEST_DAB_FREQUENCY_ID, TEST_DAB_ENSEMBLE_ID);
+ }
+
+ @Test
public void programInfoFromHalProgramInfo_withValidProgramInfo() {
android.hardware.broadcastradio.ProgramSelector halDabSelector =
AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID, new ProgramIdentifier[]{
@@ -622,11 +711,47 @@
.isEqualTo(TEST_ALBUM_ART);
}
- private static RadioManager.ModuleProperties convertToModuleProperties() {
+ @Test
+ public void getBands_withInvalidFrequency() {
+ expect.withMessage("Band for invalid frequency")
+ .that(Utils.getBand(/* freq= */ 110000)).isEqualTo(Utils.FrequencyBand.UNKNOWN);
+ }
+
+ @Test
+ public void filterToHalProgramFilter_withNullFilter() {
+ ProgramFilter filter = ConversionUtils.filterToHalProgramFilter(null);
+
+ expect.withMessage("Filter identifier types").that(filter.identifierTypes)
+ .asList().isEmpty();
+ expect.withMessage("Filter identifiers").that(filter.identifiers).asList()
+ .isEmpty();
+ }
+
+ @Test
+ public void filterToHalProgramFilter_withInvalidIdentifier() {
+ Set<ProgramSelector.Identifier> identifiers =
+ new ArraySet<ProgramSelector.Identifier>(2);
+ identifiers.add(TEST_INVALID_ID);
+ identifiers.add(TEST_DAB_SID_EXT_ID);
+ ProgramList.Filter filter = new ProgramList.Filter(/* identifierTypes */ new ArraySet<>(),
+ identifiers, /* includeCategories= */ true, /* excludeModifications= */ false);
+ ProgramFilter halFilter = ConversionUtils.filterToHalProgramFilter(filter);
+
+ expect.withMessage("Filter identifiers with invalid ones removed")
+ .that(halFilter.identifiers).asList().containsExactly(
+ ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_SID_EXT_ID));
+ }
+
+ private static RadioManager.ModuleProperties createModuleProperties() {
AmFmRegionConfig amFmConfig = createAmFmRegionConfig();
DabTableEntry[] dabTableEntries = new DabTableEntry[]{
createDabTableEntry(DAB_ENTRY_LABEL_1, DAB_ENTRY_FREQUENCY_1),
createDabTableEntry(DAB_ENTRY_LABEL_2, DAB_ENTRY_FREQUENCY_2)};
+ return createModuleProperties(amFmConfig, dabTableEntries);
+ }
+
+ private static RadioManager.ModuleProperties createModuleProperties(
+ AmFmRegionConfig amFmConfig, DabTableEntry[] dabTableEntries) {
Properties properties = createHalProperties();
return ConversionUtils.propertiesFromHalProperties(TEST_ID, TEST_SERVICE_NAME, properties,
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java
index ce27bc1..d64fcaf 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java
@@ -440,6 +440,29 @@
TEST_DAB_UNIQUE_ID_ALTERNATIVE);
}
+ @Test
+ public void filterAndApplyChunkInternal_withInvalidProgramInfoAndIdentifiers()
+ throws RemoteException {
+ ProgramInfoCache cache = new ProgramInfoCache(/* filter= */ null,
+ /* complete= */ false, TEST_FM_INFO, TEST_RDS_INFO, TEST_DAB_INFO);
+ ProgramInfo[] halModified = new android.hardware.broadcastradio.ProgramInfo[1];
+ halModified[0] = AidlTestUtils.makeHalProgramInfo(
+ ConversionUtils.programSelectorToHalProgramSelector(TEST_DAB_SELECTOR_ALTERNATIVE),
+ ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_FREQUENCY_ID_ALTERNATIVE),
+ ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_FREQUENCY_ID_ALTERNATIVE),
+ TEST_SIGNAL_QUALITY);
+ ProgramIdentifier[] halRemoved = new android.hardware.broadcastradio.ProgramIdentifier[1];
+ halRemoved[0] = new android.hardware.broadcastradio.ProgramIdentifier();
+ ProgramListChunk halChunk = AidlTestUtils.makeHalChunk(/* purge= */ false,
+ /* complete= */ true, halModified, halRemoved);
+
+ List<ProgramList.Chunk> programListChunks = cache.filterAndApplyChunkInternal(halChunk,
+ TEST_MAX_NUM_MODIFIED_PER_CHUNK, TEST_MAX_NUM_REMOVED_PER_CHUNK);
+
+ expect.withMessage("Program list chunk applied with invalid program and identifiers")
+ .that(programListChunks).isEmpty();
+ }
+
private void verifyChunkListPurge(List<ProgramList.Chunk> chunks, boolean purge) {
if (chunks.isEmpty()) {
return;
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java
index 10ac05d..a952bde 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java
@@ -16,13 +16,12 @@
package com.android.server.broadcastradio.aidl;
-import static com.google.common.truth.Truth.assertWithMessage;
-
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -32,9 +31,13 @@
import android.hardware.radio.IAnnouncementListener;
import android.hardware.radio.ICloseHandle;
import android.hardware.radio.RadioManager;
+import android.os.ParcelableException;
import android.os.RemoteException;
+import com.google.common.truth.Expect;
+
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -50,6 +53,9 @@
private static final RadioManager.ModuleProperties TEST_MODULE_PROPERTIES =
AidlTestUtils.makeDefaultModuleProperties();
+ @Rule
+ public final Expect mExpect = Expect.create();
+
// Mocks
@Mock
private IBroadcastRadio mBroadcastRadioMock;
@@ -77,13 +83,13 @@
@Test
public void getService() {
- assertWithMessage("Service of radio module")
+ mExpect.withMessage("Service of radio module")
.that(mRadioModule.getService()).isEqualTo(mBroadcastRadioMock);
}
@Test
public void getProperties() {
- assertWithMessage("Module properties of radio module")
+ mExpect.withMessage("Module properties of radio module")
.that(mRadioModule.getProperties()).isEqualTo(TEST_MODULE_PROPERTIES);
}
@@ -93,7 +99,7 @@
Bitmap imageTest = mRadioModule.getImage(imageId);
- assertWithMessage("Image from radio module").that(imageTest).isNull();
+ mExpect.withMessage("Image from radio module").that(imageTest).isNull();
}
@Test
@@ -104,7 +110,7 @@
mRadioModule.getImage(invalidImageId);
});
- assertWithMessage("Exception for getting image with invalid ID")
+ mExpect.withMessage("Exception for getting image with invalid ID")
.that(thrown).hasMessageThat().contains("Image ID is missing");
}
@@ -117,6 +123,18 @@
}
@Test
+ public void addAnnouncementListener_whenHalThrowsRemoteException() throws Exception {
+ doThrow(new RuntimeException("HAL service died")).when(mBroadcastRadioMock)
+ .registerAnnouncementListener(any(), any());
+
+ ParcelableException thrown = assertThrows(ParcelableException.class, () ->
+ mRadioModule.addAnnouncementListener(mListenerMock, new int[]{TEST_ENABLED_TYPE}));
+
+ mExpect.withMessage("Exception for adding announcement listener when HAL service died")
+ .that(thrown).hasMessageThat().contains("unknown error");
+ }
+
+ @Test
public void onListUpdate_forAnnouncementListener() throws Exception {
android.hardware.broadcastradio.Announcement halAnnouncement =
AidlTestUtils.makeAnnouncement(TEST_ENABLED_TYPE, /* selectorFreq= */ 96300);
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
index 755bcdb..4ded91d 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
@@ -421,6 +421,19 @@
}
@Test
+ public void tune_withClosedTuner_fails() throws Exception {
+ openAidlClients(/* numClients= */ 1);
+ ProgramSelector sel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
+ mTunerSessions[0].close();
+
+ IllegalStateException thrown = assertThrows(IllegalStateException.class,
+ () -> mTunerSessions[0].tune(sel));
+
+ expect.withMessage("Exception for tuning on closed tuner").that(thrown).hasMessageThat()
+ .contains("Tuner is closed");
+ }
+
+ @Test
public void step_withDirectionUp() throws Exception {
long initFreq = AM_FM_FREQUENCY_LIST[1];
ProgramSelector initialSel = AidlTestUtils.makeFmSelector(initFreq);
@@ -1149,6 +1162,20 @@
}
@Test
+ public void onCurrentProgramInfoChanged_withLowerSdkVersion_doesNotInvokesCallback()
+ throws Exception {
+ doReturn(false).when(() -> CompatChanges.isChangeEnabled(
+ eq(ConversionUtils.RADIO_U_VERSION_REQUIRED), anyInt()));
+ openAidlClients(/* numClients= */ 1);
+
+ mHalTunerCallback.onCurrentProgramInfoChanged(
+ AidlTestUtils.programInfoToHalProgramInfo(TEST_DAB_INFO));
+
+ verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).never())
+ .onCurrentProgramInfoChanged(any());
+ }
+
+ @Test
public void onTuneFailed_forTunerCallback() throws Exception {
int numSessions = 3;
openAidlClients(numSessions);
@@ -1165,6 +1192,20 @@
}
@Test
+ public void onTuneFailed_withLowerSdkVersion_doesNotInvokesCallback()
+ throws Exception {
+ doReturn(false).when(() -> CompatChanges.isChangeEnabled(
+ eq(ConversionUtils.RADIO_U_VERSION_REQUIRED), anyInt()));
+ openAidlClients(/* numClients= */ 1);
+
+ mHalTunerCallback.onTuneFailed(Result.CANCELED,
+ ConversionUtils.programSelectorToHalProgramSelector(TEST_DAB_SELECTOR));
+
+ verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).never())
+ .onTuneFailed(anyInt(), any());
+ }
+
+ @Test
public void onAntennaStateChange_forTunerCallback() throws Exception {
int numSessions = 3;
openAidlClients(numSessions);
@@ -1231,6 +1272,36 @@
}
}
+ @Test
+ public void openSession_withNonNullAntennaState() throws Exception {
+ boolean antennaConnected = false;
+ android.hardware.radio.ITunerCallback callback =
+ mock(android.hardware.radio.ITunerCallback.class);
+ openAidlClients(/* numClients= */ 1);
+ mHalTunerCallback.onAntennaStateChange(antennaConnected);
+ verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onAntennaState(antennaConnected);
+
+ mRadioModule.openSession(callback);
+
+ verify(callback, CALLBACK_TIMEOUT).onAntennaState(antennaConnected);
+ }
+
+ @Test
+ public void openSession_withNonNullCurrentProgramInfo() throws Exception {
+ openAidlClients(/* numClients= */ 1);
+ ProgramSelector initialSel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
+ RadioManager.ProgramInfo tuneInfo = AidlTestUtils.makeProgramInfo(initialSel,
+ SIGNAL_QUALITY);
+ mTunerSessions[0].tune(initialSel);
+ verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo);
+ android.hardware.radio.ITunerCallback callback =
+ mock(android.hardware.radio.ITunerCallback.class);
+
+ mRadioModule.openSession(callback);
+
+ verify(callback, CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo);
+ }
+
private void openAidlClients(int numClients) throws Exception {
mAidlTunerCallbackMocks = new android.hardware.radio.ITunerCallback[numClients];
mTunerSessions = new TunerSession[numClients];
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/AnnouncementAggregatorHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/AnnouncementAggregatorHidlTest.java
index 5e99b28..8e0abff 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/AnnouncementAggregatorHidlTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/AnnouncementAggregatorHidlTest.java
@@ -16,11 +16,11 @@
package com.android.server.broadcastradio.hal2;
-import static com.google.common.truth.Truth.assertWithMessage;
-
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -33,7 +33,10 @@
import android.os.IBinder;
import android.os.RemoteException;
+import com.google.common.truth.Expect;
+
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -55,6 +58,9 @@
private AnnouncementAggregator mAnnouncementAggregator;
private IBinder.DeathRecipient mDeathRecipient;
+ @Rule
+ public final Expect mExpect = Expect.create();
+
@Mock
private IAnnouncementListener mListenerMock;
@Mock
@@ -76,6 +82,19 @@
}
@Test
+ public void constructor_withBinderDied() throws Exception {
+ RemoteException remoteException = new RemoteException("Binder is died");
+ doThrow(remoteException).when(mBinderMock).linkToDeath(any(), anyInt());
+
+ RuntimeException thrown = assertThrows(RuntimeException.class,
+ () -> new com.android.server.broadcastradio.aidl.AnnouncementAggregator(
+ mListenerMock, mLock));
+
+ mExpect.withMessage("Exception for dead binder").that(thrown).hasMessageThat()
+ .contains(remoteException.getMessage());
+ }
+
+ @Test
public void onListUpdated_withOneModuleWatcher() throws Exception {
ArgumentCaptor<IAnnouncementListener> moduleWatcherCaptor =
ArgumentCaptor.forClass(IAnnouncementListener.class);
@@ -104,7 +123,7 @@
moduleWatcherCaptor.getValue().onListUpdated(Arrays.asList(mAnnouncementMocks[index]));
verify(mListenerMock, times(index + 1)).onListUpdated(announcementsCaptor.capture());
- assertWithMessage("Number of announcements %s after %s announcements were updated",
+ mExpect.withMessage("Number of announcements %s after %s announcements were updated",
announcementsCaptor.getValue(), index + 1)
.that(announcementsCaptor.getValue().size()).isEqualTo(index + 1);
}
@@ -132,7 +151,7 @@
() -> mAnnouncementAggregator.watchModule(mRadioModuleMocks[0],
TEST_ENABLED_TYPES));
- assertWithMessage("Exception for watching module after aggregator has been closed")
+ mExpect.withMessage("Exception for watching module after aggregator has been closed")
.that(thrown).hasMessageThat()
.contains("announcement aggregator has already been closed");
}
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java
index 3de4f5d..4cb012c 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java
@@ -21,7 +21,6 @@
import android.hardware.broadcastradio.V2_0.DabTableEntry;
import android.hardware.broadcastradio.V2_0.IdentifierType;
import android.hardware.broadcastradio.V2_0.Properties;
-import android.hardware.broadcastradio.V2_0.VendorKeyValue;
import android.hardware.radio.Announcement;
import android.hardware.radio.ProgramSelector;
import android.hardware.radio.RadioManager;
@@ -149,6 +148,26 @@
}
@Test
+ public void propertiesFromHalProperties_withInvalidBand() {
+ AmFmRegionConfig amFmRegionConfig = new AmFmRegionConfig();
+ amFmRegionConfig.ranges = new ArrayList<>(Arrays.asList(createAmFmBandRange(
+ /* lowerBound= */ 50000, /* upperBound= */ 60000, /* spacing= */ 10),
+ createAmFmBandRange(FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING)));
+
+ RadioManager.ModuleProperties properties = convertToModuleProperties(amFmRegionConfig,
+ new ArrayList<>());
+
+ RadioManager.BandDescriptor[] bands = properties.getBands();
+ expect.withMessage("Band descriptors").that(bands).hasLength(1);
+ expect.withMessage("FM band frequency lower limit")
+ .that(bands[0].getLowerLimit()).isEqualTo(FM_LOWER_LIMIT);
+ expect.withMessage("FM band frequency upper limit")
+ .that(bands[0].getUpperLimit()).isEqualTo(FM_UPPER_LIMIT);
+ expect.withMessage("FM band frequency spacing")
+ .that(bands[0].getSpacing()).isEqualTo(FM_SPACING);
+ }
+
+ @Test
public void announcementFromHalAnnouncement_typesMatch() {
expect.withMessage("Announcement type")
.that(ANNOUNCEMENT.getType()).isEqualTo(TEST_ENABLED_TYPE);
@@ -173,20 +192,31 @@
.that(ANNOUNCEMENT.getVendorInfo()).isEmpty();
}
+ @Test
+ public void getBands_withInvalidFrequency() {
+ expect.withMessage("Band for invalid frequency")
+ .that(Utils.getBand(/* freq= */ 110000)).isEqualTo(FrequencyBand.UNKNOWN);
+ }
+
private static RadioManager.ModuleProperties convertToModuleProperties() {
AmFmRegionConfig amFmConfig = createAmFmRegionConfig();
List<DabTableEntry> dabTableEntries = Arrays.asList(
createDabTableEntry(DAB_ENTRY_LABEL_1, DAB_ENTRY_FREQUENCY_1),
createDabTableEntry(DAB_ENTRY_LABEL_2, DAB_ENTRY_FREQUENCY_2));
- Properties properties = createHalProperties();
+ return convertToModuleProperties(amFmConfig, dabTableEntries);
+ }
+
+ private static RadioManager.ModuleProperties convertToModuleProperties(
+ AmFmRegionConfig amFmConfig, List<DabTableEntry> dabTableEntries) {
+ Properties properties = createHalProperties();
return Convert.propertiesFromHal(TEST_ID, TEST_SERVICE_NAME, properties,
amFmConfig, dabTableEntries);
}
private static AmFmRegionConfig createAmFmRegionConfig() {
AmFmRegionConfig amFmRegionConfig = new AmFmRegionConfig();
- amFmRegionConfig.ranges = new ArrayList<AmFmBandRange>(Arrays.asList(
+ amFmRegionConfig.ranges = new ArrayList<>(Arrays.asList(
createAmFmBandRange(FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING),
createAmFmBandRange(AM_LOWER_LIMIT, AM_UPPER_LIMIT, AM_SPACING)));
return amFmRegionConfig;
@@ -216,7 +246,7 @@
halProperties.product = TEST_PRODUCT;
halProperties.version = TEST_VERSION;
halProperties.serial = TEST_SERIAL;
- halProperties.vendorInfo = new ArrayList<VendorKeyValue>(Arrays.asList(
+ halProperties.vendorInfo = new ArrayList<>(Arrays.asList(
TestUtils.makeVendorKeyValue(VENDOR_INFO_KEY_1, VENDOR_INFO_VALUE_1),
TestUtils.makeVendorKeyValue(VENDOR_INFO_KEY_2, VENDOR_INFO_VALUE_2)));
return halProperties;
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java
index 36a6430..015e9c0 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java
@@ -17,6 +17,7 @@
import static org.junit.Assert.*;
+import android.hardware.broadcastradio.V2_0.ProgramIdentifier;
import android.hardware.broadcastradio.V2_0.ProgramListChunk;
import android.hardware.radio.ProgramList;
import android.hardware.radio.ProgramSelector;
@@ -34,6 +35,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -261,6 +263,25 @@
verifyChunkListRemoved(chunks, 1, TEST_DAB_UNIQUE_ID, TEST_VENDOR_UNIQUE_ID);
}
+ @Test
+ public void filterAndApplyChunkInternal_withInvalidIdentifier() {
+ ProgramInfoCache cache = new ProgramInfoCache(/* filter= */ null, /* complete= */ false,
+ TEST_AM_FM_INFO, TEST_RDS_INFO, TEST_DAB_INFO, TEST_VENDOR_INFO);
+ ArrayList<ProgramIdentifier> halRemoved = new ArrayList<>();
+ halRemoved.add(new ProgramIdentifier());
+ ProgramListChunk halChunk = new ProgramListChunk();
+ halChunk.complete = true;
+ halChunk.purge = false;
+ halChunk.modified = new ArrayList<>();
+ halChunk.removed = halRemoved;
+
+ List<ProgramList.Chunk> programListChunks = cache.filterAndApplyChunkInternal(halChunk,
+ /* maxNumModifiedPerChunk= */ 1, /* maxNumRemovedPerChunk= */ 1);
+
+ expect.withMessage("Program list chunk applied with invalid identifier")
+ .that(programListChunks).isEmpty();
+ }
+
// Verifies that:
// - The first chunk's purge flag matches expectPurge.
// - The last chunk's complete flag matches expectComplete.
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
index 6edfa02..898ef57 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
@@ -29,8 +29,6 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.timeout;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.google.common.truth.Truth.assertWithMessage;
-
import static org.junit.Assert.assertThrows;
import android.graphics.Bitmap;
@@ -57,8 +55,11 @@
import com.android.server.broadcastradio.ExtendedRadioMockitoTestCase;
import com.android.server.broadcastradio.RadioServiceUserController;
+import com.google.common.truth.Expect;
+
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -98,6 +99,9 @@
private ProgramInfo mHalCurrentInfo;
private TunerSession[] mTunerSessions;
+ @Rule
+ public final Expect mExpect = Expect.create();
+
@Mock
private UserHandle mUserHandleMock;
@Mock
@@ -206,7 +210,7 @@
openAidlClients(numSessions);
for (int index = 0; index < numSessions; index++) {
- assertWithMessage("Session of index %s close state", index)
+ mExpect.withMessage("Session of index %s close state", index)
.that(mTunerSessions[index].isClosed()).isFalse();
}
}
@@ -238,7 +242,7 @@
RadioManager.BandConfig config = mTunerSessions[0].getConfiguration();
- assertWithMessage("Session configuration").that(config)
+ mExpect.withMessage("Session configuration").that(config)
.isEqualTo(FM_BAND_CONFIG);
}
@@ -248,7 +252,7 @@
mTunerSessions[0].setMuted(/* mute= */ false);
- assertWithMessage("Session mute state after setting unmuted")
+ mExpect.withMessage("Session mute state after setting unmuted")
.that(mTunerSessions[0].isMuted()).isFalse();
}
@@ -258,7 +262,7 @@
mTunerSessions[0].setMuted(/* mute= */ true);
- assertWithMessage("Session mute state after setting muted")
+ mExpect.withMessage("Session mute state after setting muted")
.that(mTunerSessions[0].isMuted()).isTrue();
}
@@ -268,7 +272,7 @@
mTunerSessions[0].close();
- assertWithMessage("Close state of broadcast radio service session")
+ mExpect.withMessage("Close state of broadcast radio service session")
.that(mTunerSessions[0].isClosed()).isTrue();
}
@@ -282,11 +286,11 @@
for (int index = 0; index < numSessions; index++) {
if (index == closeIdx) {
- assertWithMessage(
+ mExpect.withMessage(
"Close state of broadcast radio service session of index %s", index)
.that(mTunerSessions[index].isClosed()).isTrue();
} else {
- assertWithMessage(
+ mExpect.withMessage(
"Close state of broadcast radio service session of index %s", index)
.that(mTunerSessions[index].isClosed()).isFalse();
}
@@ -301,7 +305,21 @@
mTunerSessions[0].close(errorCode);
verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onError(errorCode);
- assertWithMessage("Close state of broadcast radio service session")
+ mExpect.withMessage("Close state of broadcast radio service session")
+ .that(mTunerSessions[0].isClosed()).isTrue();
+ }
+
+ @Test
+ public void close_forMultipleTimes() throws Exception {
+ openAidlClients(/* numClients= */ 1);
+ int errorCode = RadioTuner.ERROR_SERVER_DIED;
+ mTunerSessions[0].close(errorCode);
+ verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onError(errorCode);
+
+ mTunerSessions[0].close(errorCode);
+
+ verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onError(errorCode);
+ mExpect.withMessage("State of closing broadcast radio service session twice")
.that(mTunerSessions[0].isClosed()).isTrue();
}
@@ -315,7 +333,7 @@
for (int index = 0; index < numSessions; index++) {
verify(mAidlTunerCallbackMocks[index], CALLBACK_TIMEOUT).onError(errorCode);
- assertWithMessage("Close state of broadcast radio service session of index %s", index)
+ mExpect.withMessage("Close state of broadcast radio service session of index %s", index)
.that(mTunerSessions[index].isClosed()).isTrue();
}
}
@@ -365,7 +383,7 @@
UnsupportedOperationException thrown = assertThrows(UnsupportedOperationException.class,
() -> mTunerSessions[0].tune(unsupportedSelector));
- assertWithMessage("Exception for tuning on unsupported program selector")
+ mExpect.withMessage("Exception for tuning on unsupported program selector")
.that(thrown).hasMessageThat().contains("tune: NOT_SUPPORTED");
}
@@ -393,11 +411,24 @@
mTunerSessions[0].tune(sel);
});
- assertWithMessage("Unknown error HAL exception when tuning")
+ mExpect.withMessage("Unknown error HAL exception when tuning")
.that(thrown).hasMessageThat().contains(Result.toString(Result.UNKNOWN_ERROR));
}
@Test
+ public void tune_withClosedTuner_fails() throws Exception {
+ openAidlClients(/* numClients= */ 1);
+ ProgramSelector sel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
+ mTunerSessions[0].close();
+
+ IllegalStateException thrown = assertThrows(IllegalStateException.class,
+ () -> mTunerSessions[0].tune(sel));
+
+ mExpect.withMessage("Exception for tuning on closed tuner").that(thrown).hasMessageThat()
+ .contains("Tuner is closed");
+ }
+
+ @Test
public void step_withDirectionUp() throws Exception {
long initFreq = AM_FM_FREQUENCY_LIST[1];
ProgramSelector initialSel = TestUtils.makeFmSelector(initFreq);
@@ -454,7 +485,7 @@
mTunerSessions[0].step(/* directionDown= */ true, /* skipSubChannel= */ false);
});
- assertWithMessage("Exception for stepping when HAL is in invalid state")
+ mExpect.withMessage("Exception for stepping when HAL is in invalid state")
.that(thrown).hasMessageThat().contains(Result.toString(Result.INVALID_STATE));
}
@@ -533,7 +564,7 @@
mTunerSessions[0].seek(/* directionDown= */ true, /* skipSubChannel= */ false);
});
- assertWithMessage("Internal error HAL exception when seeking")
+ mExpect.withMessage("Internal error HAL exception when seeking")
.that(thrown).hasMessageThat().contains(Result.toString(Result.INTERNAL_ERROR));
}
@@ -566,7 +597,7 @@
mTunerSessions[0].cancel();
});
- assertWithMessage("Exception for canceling when HAL throws remote exception")
+ mExpect.withMessage("Exception for canceling when HAL throws remote exception")
.that(thrown).hasMessageThat().contains(exceptionMessage);
}
@@ -579,7 +610,7 @@
mTunerSessions[0].getImage(imageId);
});
- assertWithMessage("Get image exception")
+ mExpect.withMessage("Get image exception")
.that(thrown).hasMessageThat().contains("Image ID is missing");
}
@@ -590,7 +621,7 @@
Bitmap imageTest = mTunerSessions[0].getImage(imageId);
- assertWithMessage("Null image").that(imageTest).isEqualTo(null);
+ mExpect.withMessage("Null image").that(imageTest).isEqualTo(null);
}
@Test
@@ -603,7 +634,7 @@
mTunerSessions[0].getImage(/* id= */ 1);
});
- assertWithMessage("Exception for getting image when HAL throws remote exception")
+ mExpect.withMessage("Exception for getting image when HAL throws remote exception")
.that(thrown).hasMessageThat().contains(exceptionMessage);
}
@@ -649,7 +680,7 @@
mTunerSessions[0].startProgramListUpdates(/* filter= */ null);
});
- assertWithMessage("Unknown error HAL exception when updating program list")
+ mExpect.withMessage("Unknown error HAL exception when updating program list")
.that(thrown).hasMessageThat().contains(Result.toString(Result.UNKNOWN_ERROR));
}
@@ -686,7 +717,7 @@
boolean isSupported = mTunerSessions[0].isConfigFlagSupported(flag);
verify(mHalTunerSessionMock).isConfigFlagSet(eq(flag), any());
- assertWithMessage("Config flag %s is supported", flag).that(isSupported).isFalse();
+ mExpect.withMessage("Config flag %s is supported", flag).that(isSupported).isFalse();
}
@Test
@@ -697,7 +728,7 @@
boolean isSupported = mTunerSessions[0].isConfigFlagSupported(flag);
verify(mHalTunerSessionMock).isConfigFlagSet(eq(flag), any());
- assertWithMessage("Config flag %s is supported", flag).that(isSupported).isTrue();
+ mExpect.withMessage("Config flag %s is supported", flag).that(isSupported).isTrue();
}
@Test
@@ -709,7 +740,7 @@
mTunerSessions[0].setConfigFlag(flag, /* value= */ true);
});
- assertWithMessage("Exception for setting unsupported flag %s", flag)
+ mExpect.withMessage("Exception for setting unsupported flag %s", flag)
.that(thrown).hasMessageThat().contains("setConfigFlag: NOT_SUPPORTED");
}
@@ -755,7 +786,7 @@
mTunerSessions[0].isConfigFlagSet(flag);
});
- assertWithMessage("Exception for checking if unsupported flag %s is set", flag)
+ mExpect.withMessage("Exception for checking if unsupported flag %s is set", flag)
.that(thrown).hasMessageThat().contains("isConfigFlagSet: NOT_SUPPORTED");
}
@@ -768,7 +799,7 @@
boolean isSet = mTunerSessions[0].isConfigFlagSet(flag);
- assertWithMessage("Config flag %s is set", flag)
+ mExpect.withMessage("Config flag %s is set", flag)
.that(isSet).isEqualTo(expectedConfigFlagValue);
}
@@ -782,7 +813,7 @@
mTunerSessions[0].isConfigFlagSet(flag);
});
- assertWithMessage("Exception for checking config flag when HAL throws remote exception")
+ mExpect.withMessage("Exception for checking config flag when HAL throws remote exception")
.that(thrown).hasMessageThat().contains("Failed to check flag");
}
@@ -822,7 +853,7 @@
mTunerSessions[0].setParameters(parametersSet);
});
- assertWithMessage("Exception for setting parameters when HAL throws remote exception")
+ mExpect.withMessage("Exception for setting parameters when HAL throws remote exception")
.that(thrown).hasMessageThat().contains(exceptionMessage);
}
@@ -848,7 +879,7 @@
mTunerSessions[0].getParameters(parameterKeys);
});
- assertWithMessage("Exception for getting parameters when HAL throws remote exception")
+ mExpect.withMessage("Exception for getting parameters when HAL throws remote exception")
.that(thrown).hasMessageThat().contains(exceptionMessage);
}
@@ -894,6 +925,36 @@
}
}
+ @Test
+ public void openSession_withNonNullAntennaState() throws Exception {
+ boolean antennaConnected = false;
+ android.hardware.radio.ITunerCallback callback =
+ mock(android.hardware.radio.ITunerCallback.class);
+ openAidlClients(/* numClients= */ 1);
+ mHalTunerCallback.onAntennaStateChange(antennaConnected);
+ verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onAntennaState(antennaConnected);
+
+ mRadioModule.openSession(callback);
+
+ verify(callback, CALLBACK_TIMEOUT).onAntennaState(antennaConnected);
+ }
+
+ @Test
+ public void openSession_withNonNullCurrentProgramInfo() throws Exception {
+ openAidlClients(/* numClients= */ 1);
+ ProgramSelector initialSel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
+ RadioManager.ProgramInfo tuneInfo = TestUtils.makeProgramInfo(initialSel,
+ SIGNAL_QUALITY);
+ mTunerSessions[0].tune(initialSel);
+ verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo);
+ android.hardware.radio.ITunerCallback callback =
+ mock(android.hardware.radio.ITunerCallback.class);
+
+ mRadioModule.openSession(callback);
+
+ verify(callback, CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo);
+ }
+
private void openAidlClients(int numClients) throws Exception {
mAidlTunerCallbackMocks = new android.hardware.radio.ITunerCallback[numClients];
mTunerSessions = new TunerSession[numClients];
diff --git a/core/tests/coretests/src/android/view/ViewFrameRateTest.java b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
index 35b984a..169300a 100644
--- a/core/tests/coretests/src/android/view/ViewFrameRateTest.java
+++ b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
@@ -45,6 +45,7 @@
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.sysprop.ViewProperties;
import android.util.DisplayMetrics;
import android.widget.FrameLayout;
import android.widget.ProgressBar;
@@ -101,6 +102,9 @@
@RequiresFlagsEnabled({FLAG_VIEW_VELOCITY_API,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void frameRateChangesWhenContentMoves() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
waitForFrameRateCategoryToSettle();
mActivityRule.runOnUiThread(() -> {
mMovingView.offsetLeftAndRight(100);
@@ -127,6 +131,9 @@
@Test
@RequiresFlagsEnabled(FLAG_VIEW_VELOCITY_API)
public void frameBoostDisable() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
mActivityRule.runOnUiThread(() -> {
long now = SystemClock.uptimeMillis();
MotionEvent down = MotionEvent.obtain(
@@ -155,6 +162,9 @@
FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void lowVelocity60() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
mActivityRule.runOnUiThread(() -> {
ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams();
layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
@@ -175,6 +185,9 @@
FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void velocityWithChildMovement() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
FrameLayout frameLayout = new FrameLayout(mActivity);
mActivityRule.runOnUiThread(() -> {
ViewGroup.LayoutParams fullSize = new ViewGroup.LayoutParams(
@@ -201,6 +214,9 @@
FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void highVelocity120() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
mActivityRule.runOnUiThread(() -> {
ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams();
layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
@@ -222,6 +238,9 @@
@RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void noVelocityUsesCategorySmall() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
final CountDownLatch drawLatch1 = new CountDownLatch(1);
mActivityRule.runOnUiThread(() -> {
DisplayMetrics displayMetrics = mActivity.getResources().getDisplayMetrics();
@@ -259,6 +278,9 @@
@RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void noVelocityUsesCategoryNarrowWidth() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
final CountDownLatch drawLatch1 = new CountDownLatch(1);
mActivityRule.runOnUiThread(() -> {
DisplayMetrics displayMetrics = mActivity.getResources().getDisplayMetrics();
@@ -295,6 +317,9 @@
@RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void noVelocityUsesCategoryNarrowHeight() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
final CountDownLatch drawLatch1 = new CountDownLatch(1);
mActivityRule.runOnUiThread(() -> {
DisplayMetrics displayMetrics = mActivity.getResources().getDisplayMetrics();
@@ -331,6 +356,9 @@
@RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void noVelocityUsesCategoryLargeWidth() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
final CountDownLatch drawLatch1 = new CountDownLatch(1);
mActivityRule.runOnUiThread(() -> {
DisplayMetrics displayMetrics = mActivity.getResources().getDisplayMetrics();
@@ -367,6 +395,9 @@
@RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void noVelocityUsesCategoryLargeHeight() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
final CountDownLatch drawLatch1 = new CountDownLatch(1);
mActivityRule.runOnUiThread(() -> {
DisplayMetrics displayMetrics = mActivity.getResources().getDisplayMetrics();
@@ -403,6 +434,9 @@
@RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void defaultNormal() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
mActivityRule.runOnUiThread(() -> {
View parent = (View) mMovingView.getParent();
ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams();
@@ -427,6 +461,9 @@
FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY
})
public void frameRateAndCategory() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
waitForFrameRateCategoryToSettle();
mActivityRule.runOnUiThread(() -> {
@@ -447,6 +484,9 @@
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY
})
public void willNotDrawUsesCategory() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
mActivityRule.runOnUiThread(() -> {
mMovingView.setWillNotDraw(true);
mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_LOW);
@@ -480,6 +520,9 @@
@RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void intermittentDoubleInvalidate() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
View parent = (View) mMovingView.getParent();
mActivityRule.runOnUiThread(() -> {
parent.setWillNotDraw(false);
@@ -526,6 +569,9 @@
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY
})
public void sameFrameMotion() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
waitForFrameRateCategoryToSettle();
@@ -549,6 +595,9 @@
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY
})
public void frameRateReset() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
mMovingView.setRequestedFrameRate(120f);
waitForFrameRateCategoryToSettle();
mActivityRule.runOnUiThread(() -> mMovingView.setVisibility(View.INVISIBLE));
@@ -570,6 +619,9 @@
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY
})
public void frameRateResetWithInvalidations() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
mMovingView.setRequestedFrameRate(120f);
waitForFrameRateCategoryToSettle();
mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NORMAL);
@@ -590,6 +642,9 @@
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY
})
public void testQuickTouchBoost() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
mActivityRule.runOnUiThread(() -> {
mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_LOW);
ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams();
@@ -630,6 +685,9 @@
com.android.graphics.surfaceflinger.flags.Flags.FLAG_VRR_BUGFIX_24Q4
})
public void idleDetected() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
waitForFrameRateCategoryToSettle();
mActivityRule.runOnUiThread(() -> {
mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_HIGH);
@@ -654,6 +712,9 @@
com.android.graphics.surfaceflinger.flags.Flags.FLAG_VRR_BUGFIX_24Q4
})
public void vectorDrawableFrameRate() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
final ProgressBar[] progressBars = new ProgressBar[3];
final ViewGroup[] parents = new ViewGroup[1];
mActivityRule.runOnUiThread(() -> {
@@ -711,6 +772,9 @@
com.android.graphics.surfaceflinger.flags.Flags.FLAG_VRR_BUGFIX_24Q4
})
public void renderNodeAnimatorFrameRateCanceled() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
waitForFrameRateCategoryToSettle();
@@ -748,6 +812,9 @@
com.android.graphics.surfaceflinger.flags.Flags.FLAG_VRR_BUGFIX_24Q4
})
public void renderNodeAnimatorFrameRateRemoved() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
waitForFrameRateCategoryToSettle();
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 94e187a..06cb0ee 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -72,6 +72,7 @@
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
+import android.sysprop.ViewProperties;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.WindowInsets.Side;
@@ -503,6 +504,9 @@
@RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void votePreferredFrameRate_getDefaultValues() {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
ViewRootImpl viewRootImpl = new ViewRootImpl(sContext,
sContext.getDisplayNoVerify());
assertEquals(FRAME_RATE_CATEGORY_DEFAULT,
@@ -521,6 +525,9 @@
FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void votePreferredFrameRate_voteFrameRateCategory_visibility_bySize() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
mView = new View(sContext);
attachViewToWindow(mView);
mViewRootImpl = mView.getViewRootImpl();
@@ -558,6 +565,9 @@
FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void votePreferredFrameRate_voteFrameRateCategory_smallSize_bySize() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
mView = new View(sContext);
WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
@@ -590,6 +600,9 @@
FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void votePreferredFrameRate_voteFrameRateCategory_normalSize_bySize() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
mView = new View(sContext);
WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
@@ -627,6 +640,9 @@
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void votePreferredFrameRate_voteFrameRateCategory_visibility_defaultHigh()
throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
mView = new View(sContext);
WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
@@ -688,6 +704,9 @@
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void votePreferredFrameRate_voteFrameRateCategory_smallSize_defaultHigh()
throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
mView = new View(sContext);
WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
@@ -723,6 +742,9 @@
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void votePreferredFrameRate_voteFrameRateCategory_normalSize_defaultHigh()
throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
mView = new View(sContext);
WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
@@ -758,6 +780,9 @@
@RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void votePreferredFrameRate_voteFrameRateCategory_aggregate() {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
mView = new View(sContext);
attachViewToWindow(mView);
mViewRootImpl = mView.getViewRootImpl();
@@ -804,6 +829,9 @@
@RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void votePreferredFrameRate_voteFrameRate_aggregate() {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
mView = new View(sContext);
attachViewToWindow(mView);
mViewRootImpl = mView.getViewRootImpl();
@@ -876,6 +904,9 @@
FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void votePreferredFrameRate_voteFrameRate_category() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
mView = new View(sContext);
attachViewToWindow(mView);
sInstrumentation.waitForIdleSync();
@@ -930,6 +961,9 @@
FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void votePreferredFrameRate_voteFrameRateCategory_velocityToHigh() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
mView = new View(sContext);
WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
@@ -973,6 +1007,9 @@
@RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void votePreferredFrameRate_insetsAnimation() {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
mView = new View(sContext);
WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
@@ -1010,6 +1047,9 @@
@RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void votePreferredFrameRate_frameRateBoostOnTouch() {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
mView = new View(sContext);
attachViewToWindow(mView);
sInstrumentation.waitForIdleSync();
@@ -1043,6 +1083,9 @@
@RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void votePreferredFrameRate_voteFrameRateTimeOut() throws InterruptedException {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
final long delay = 200L;
mView = new View(sContext);
@@ -1082,6 +1125,9 @@
FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void votePreferredFrameRate_voteFrameRateOnly() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
mView = new View(sContext);
float frameRate = 20;
attachViewToWindow(mView);
@@ -1133,6 +1179,9 @@
FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void votePreferredFrameRate_infrequentLayer_defaultHigh() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
final long delay = 200L;
mView = new View(sContext);
@@ -1175,11 +1224,8 @@
// Infrequent update
Thread.sleep(delay);
- // Even though this is not a small View, step 3 is triggered by this flag, which
- // brings intermittent to LOW
- int intermittentExpected = toolkitFrameRateBySizeReadOnly()
- ? FRAME_RATE_CATEGORY_LOW
- : FRAME_RATE_CATEGORY_NORMAL;
+ // The expected category is normal for intermittent.
+ int intermittentExpected = FRAME_RATE_CATEGORY_NORMAL;
sInstrumentation.runOnMainSync(() -> {
mView.invalidate();
@@ -1211,6 +1257,9 @@
@RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void votePreferredFrameRate_isFrameRatePowerSavingsBalanced() {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
mView = new View(sContext);
attachViewToWindow(mView);
sInstrumentation.waitForIdleSync();
@@ -1245,6 +1294,9 @@
FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY,
FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
public void votePreferredFrameRate_applyTextureViewHeuristic() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
final long delay = 30L;
mView = new TextureView(sContext);
@@ -1289,6 +1341,9 @@
@Test
@RequiresFlagsEnabled(FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY)
public void votePreferredFrameRate_velocityVotedAfterOnDraw() throws Throwable {
+ if (!ViewProperties.vrr_enabled().orElse(true)) {
+ return;
+ }
mView = new View(sContext);
double delta = 0.1;
float pixelsPerSecond = 1000_000;
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
index 15c9047..c8ea374 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
@@ -34,6 +34,9 @@
import android.appwidget.AppWidgetHostView;
import android.content.Context;
import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.AsyncTask;
@@ -834,33 +837,6 @@
}
@Test
- public void visitUris_intents() {
- RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_test);
-
- Uri fillIntentUri = Uri.parse("content://intent/fill");
- views.setOnCheckedChangeResponse(
- R.id.layout,
- RemoteViews.RemoteResponse.fromFillInIntent(new Intent("action", fillIntentUri)));
-
- Uri pendingIntentUri = Uri.parse("content://intent/pending");
- PendingIntent pendingIntent = getPendingIntentWithUri(pendingIntentUri);
- views.setOnClickResponse(
- R.id.layout,
- RemoteViews.RemoteResponse.fromPendingIntent(pendingIntent));
-
- Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class);
- views.visitUris(visitor);
- verify(visitor, times(1)).accept(eq(fillIntentUri));
- verify(visitor, times(1)).accept(eq(pendingIntentUri));
- }
-
- private PendingIntent getPendingIntentWithUri(Uri uri) {
- return PendingIntent.getActivity(mContext, 0,
- new Intent("action", uri),
- PendingIntent.FLAG_IMMUTABLE);
- }
-
- @Test
public void layoutInflaterFactory_nothingSet_returnsNull() {
final RemoteViews rv = new RemoteViews(mPackage, R.layout.remote_views_test);
assertNull(rv.getLayoutInflaterFactory());
diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
index d4482f2..b0190a5 100644
--- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
+++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
@@ -112,7 +112,7 @@
doReturn(mApplicationInfo).when(mContext).getApplicationInfo();
mDispatcher = new WindowOnBackInvokedDispatcher(mContext, Looper.getMainLooper());
- mDispatcher.attachToWindow(mWindowSession, mWindow, null, mImeBackAnimationController);
+ mDispatcher.attachToWindow(mWindowSession, mWindow, mImeBackAnimationController);
}
private void waitForIdle() {
@@ -455,26 +455,25 @@
@Test
public void registerImeCallbacks_onBackInvokedCallbackEnabled() throws RemoteException {
- verifyImeCallackRegistrations();
- }
-
- @Test
- public void registerImeCallbacks_onBackInvokedCallbackDisabled() throws RemoteException {
- doReturn(false).when(mApplicationInfo).isOnBackInvokedCallbackEnabled();
- verifyImeCallackRegistrations();
- }
-
- private void verifyImeCallackRegistrations() throws RemoteException {
- // verify default callback is replaced with ImeBackAnimationController
- mDispatcher.registerOnBackInvokedCallbackUnchecked(mDefaultImeCallback, PRIORITY_DEFAULT);
+ mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mDefaultImeCallback);
assertCallbacksSize(/* default */ 1, /* overlay */ 0);
assertSetCallbackInfo();
assertTopCallback(mImeBackAnimationController);
- // verify regular ime callback is successfully registered
- mDispatcher.registerOnBackInvokedCallbackUnchecked(mImeCallback, PRIORITY_DEFAULT);
+ mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mImeCallback);
assertCallbacksSize(/* default */ 2, /* overlay */ 0);
assertSetCallbackInfo();
assertTopCallback(mImeCallback);
}
+
+ @Test
+ public void registerImeCallbacks_legacyBack() throws RemoteException {
+ doReturn(false).when(mApplicationInfo).isOnBackInvokedCallbackEnabled();
+
+ mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mDefaultImeCallback);
+ assertNoSetCallbackInfo();
+
+ mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mImeCallback);
+ assertNoSetCallbackInfo();
+ }
}
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiDeviceInfoTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiDeviceInfoTest.java
old mode 100755
new mode 100644
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiPortInfoTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiPortInfoTest.java
old mode 100755
new mode 100644
diff --git a/data/sounds/alarms/material/ogg/Awaken_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Awaken_OG7_1ch_48k.ogg
old mode 100755
new mode 100644
Binary files differ
diff --git a/data/sounds/alarms/material/ogg/Bounce_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Bounce_OG7_1ch_48k.ogg
old mode 100755
new mode 100644
Binary files differ
diff --git a/data/sounds/alarms/material/ogg/Drip_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Drip_OG7_1ch_48k.ogg
old mode 100755
new mode 100644
Binary files differ
diff --git a/data/sounds/alarms/material/ogg/Gallop_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Gallop_OG7_1ch_48k.ogg
old mode 100755
new mode 100644
Binary files differ
diff --git a/data/sounds/alarms/material/ogg/Nudge_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Nudge_OG7_1ch_48k.ogg
old mode 100755
new mode 100644
Binary files differ
diff --git a/data/sounds/alarms/material/ogg/Orbit_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Orbit_OG7_1ch_48k.ogg
old mode 100755
new mode 100644
Binary files differ
diff --git a/data/sounds/alarms/material/ogg/Rise_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Rise_OG7_1ch_48k.ogg
old mode 100755
new mode 100644
Binary files differ
diff --git a/data/sounds/alarms/material/ogg/Sway_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Sway_OG7_1ch_48k.ogg
old mode 100755
new mode 100644
Binary files differ
diff --git a/data/sounds/alarms/material/ogg/Wag_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Wag_OG7_1ch_48k.ogg
old mode 100755
new mode 100644
Binary files differ
diff --git a/docs/downloads/training/LocationUpdates.zip b/docs/downloads/training/LocationUpdates.zip
old mode 100755
new mode 100644
Binary files differ
diff --git a/graphics/java/android/framework_graphics.aconfig b/graphics/java/android/framework_graphics.aconfig
index 0b9e72d..5ad97e6 100644
--- a/graphics/java/android/framework_graphics.aconfig
+++ b/graphics/java/android/framework_graphics.aconfig
@@ -24,3 +24,12 @@
description: "Return null when decode from URI fails in Icon.loadDrawable()"
bug: "335878768"
}
+
+flag {
+ name: "ok_lab_colorspace"
+ is_exported: true
+ is_fixed_read_only: true
+ namespace: "core_graphics"
+ description: "Add OkLab ColorSpace support"
+ bug: "344038816"
+}
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index 4bc3ece..3752257 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -17,6 +17,7 @@
package android.graphics;
import android.annotation.AnyThread;
+import android.annotation.FlaggedApi;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -27,9 +28,13 @@
import android.hardware.DataSpace.ColorDataSpace;
import android.util.SparseIntArray;
+import com.android.graphics.flags.Flags;
+
import libcore.util.NativeAllocationRegistry;
import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
import java.util.function.DoubleUnaryOperator;
/**
@@ -230,7 +235,9 @@
-2392 / 128.0, 8192 / 1305.0, Rgb.TransferParameters.TYPE_PQish);
// See static initialization block next to #get(Named)
- private static final ColorSpace[] sNamedColorSpaces = new ColorSpace[Named.values().length];
+ private static final HashMap<Integer, ColorSpace> sNamedColorSpaceMap =
+ new HashMap<>();
+
private static final SparseIntArray sDataToColorSpaces = new SparseIntArray();
@NonNull private final String mName;
@@ -745,7 +752,23 @@
* <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr>
* </table>
*/
- BT2020_PQ
+ BT2020_PQ,
+
+ /**
+ * <p>{@link ColorSpace.Lab Lab} color space OkLab standardized as
+ * OkLab</p>
+ * <table summary="Color space definition">
+ * <tr><th>Property</th><th colspan="4">Value</th></tr>
+ * <tr><td>Name</td><td colspan="4">Oklab</td></tr>
+ * <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr>
+ * <tr>
+ * <td>Range</td>
+ * <td colspan="4">\(L: `[0.0, 1.0]`, a: `[-2, 2]`, b: `[-2, 2]`\)</td>
+ * </tr>
+ * </table>
+ */
+ @FlaggedApi(Flags.FLAG_OK_LAB_COLORSPACE)
+ OK_LAB
// Update the initialization block next to #get(Named) when adding new values
}
@@ -1428,11 +1451,11 @@
*/
@NonNull
static ColorSpace get(@IntRange(from = MIN_ID, to = MAX_ID) int index) {
- if (index < 0 || index >= sNamedColorSpaces.length) {
- throw new IllegalArgumentException("Invalid ID, must be in the range [0.." +
- sNamedColorSpaces.length + ")");
+ ColorSpace colorspace = sNamedColorSpaceMap.get(index);
+ if (colorspace == null) {
+ throw new IllegalArgumentException("Invalid ID: " + index);
}
- return sNamedColorSpaces[index];
+ return colorspace;
}
/**
@@ -1485,12 +1508,20 @@
*
* <p>This method is thread-safe.</p>
*
+ * Note that in the Android W release and later, this can return the SRGB ColorSpace if
+ * the {@link ColorSpace.Named} parameter is not enabled in the corresponding release.
+ *
* @param name The name of the color space to get an instance of
- * @return A non-null {@link ColorSpace} instance
+ * @return A non-null {@link ColorSpace} instance. If the ColorSpace is not supported
+ * then the SRGB ColorSpace is returned.
*/
@NonNull
public static ColorSpace get(@NonNull Named name) {
- return sNamedColorSpaces[name.ordinal()];
+ ColorSpace colorSpace = sNamedColorSpaceMap.get(name.ordinal());
+ if (colorSpace == null) {
+ return sNamedColorSpaceMap.get(Named.SRGB.ordinal());
+ }
+ return colorSpace;
}
/**
@@ -1511,7 +1542,8 @@
@NonNull @Size(9) float[] toXYZD50,
@NonNull Rgb.TransferParameters function) {
- for (ColorSpace colorSpace : sNamedColorSpaces) {
+ Collection<ColorSpace> colorspaces = sNamedColorSpaceMap.values();
+ for (ColorSpace colorSpace : colorspaces) {
if (colorSpace.getModel() == Model.RGB) {
ColorSpace.Rgb rgb = (ColorSpace.Rgb) adapt(colorSpace, ILLUMINANT_D50_XYZ);
if (compare(toXYZD50, rgb.mTransform) &&
@@ -1525,25 +1557,25 @@
}
static {
- sNamedColorSpaces[Named.SRGB.ordinal()] = new ColorSpace.Rgb(
+ sNamedColorSpaceMap.put(Named.SRGB.ordinal(), new ColorSpace.Rgb(
"sRGB IEC61966-2.1",
SRGB_PRIMARIES,
ILLUMINANT_D65,
null,
SRGB_TRANSFER_PARAMETERS,
Named.SRGB.ordinal()
- );
+ ));
sDataToColorSpaces.put(DataSpace.DATASPACE_SRGB, Named.SRGB.ordinal());
- sNamedColorSpaces[Named.LINEAR_SRGB.ordinal()] = new ColorSpace.Rgb(
+ sNamedColorSpaceMap.put(Named.LINEAR_SRGB.ordinal(), new ColorSpace.Rgb(
"sRGB IEC61966-2.1 (Linear)",
SRGB_PRIMARIES,
ILLUMINANT_D65,
1.0,
0.0f, 1.0f,
Named.LINEAR_SRGB.ordinal()
- );
+ ));
sDataToColorSpaces.put(DataSpace.DATASPACE_SRGB_LINEAR, Named.LINEAR_SRGB.ordinal());
- sNamedColorSpaces[Named.EXTENDED_SRGB.ordinal()] = new ColorSpace.Rgb(
+ sNamedColorSpaceMap.put(Named.EXTENDED_SRGB.ordinal(), new ColorSpace.Rgb(
"scRGB-nl IEC 61966-2-2:2003",
SRGB_PRIMARIES,
ILLUMINANT_D65,
@@ -1553,112 +1585,113 @@
-0.799f, 2.399f,
SRGB_TRANSFER_PARAMETERS,
Named.EXTENDED_SRGB.ordinal()
- );
+ ));
sDataToColorSpaces.put(DataSpace.DATASPACE_SCRGB, Named.EXTENDED_SRGB.ordinal());
- sNamedColorSpaces[Named.LINEAR_EXTENDED_SRGB.ordinal()] = new ColorSpace.Rgb(
+ sNamedColorSpaceMap.put(Named.LINEAR_EXTENDED_SRGB.ordinal(), new ColorSpace.Rgb(
"scRGB IEC 61966-2-2:2003",
SRGB_PRIMARIES,
ILLUMINANT_D65,
1.0,
-0.5f, 7.499f,
Named.LINEAR_EXTENDED_SRGB.ordinal()
- );
+ ));
sDataToColorSpaces.put(
DataSpace.DATASPACE_SCRGB_LINEAR, Named.LINEAR_EXTENDED_SRGB.ordinal());
- sNamedColorSpaces[Named.BT709.ordinal()] = new ColorSpace.Rgb(
+ sNamedColorSpaceMap.put(Named.BT709.ordinal(), new ColorSpace.Rgb(
"Rec. ITU-R BT.709-5",
SRGB_PRIMARIES,
ILLUMINANT_D65,
null,
SMPTE_170M_TRANSFER_PARAMETERS,
Named.BT709.ordinal()
- );
+ ));
sDataToColorSpaces.put(DataSpace.DATASPACE_BT709, Named.BT709.ordinal());
- sNamedColorSpaces[Named.BT2020.ordinal()] = new ColorSpace.Rgb(
+ sNamedColorSpaceMap.put(Named.BT2020.ordinal(), new ColorSpace.Rgb(
"Rec. ITU-R BT.2020-1",
BT2020_PRIMARIES,
ILLUMINANT_D65,
null,
new Rgb.TransferParameters(1 / 1.0993, 0.0993 / 1.0993, 1 / 4.5, 0.08145, 1 / 0.45),
Named.BT2020.ordinal()
- );
+ ));
+
sDataToColorSpaces.put(DataSpace.DATASPACE_BT2020, Named.BT2020.ordinal());
- sNamedColorSpaces[Named.DCI_P3.ordinal()] = new ColorSpace.Rgb(
+ sNamedColorSpaceMap.put(Named.DCI_P3.ordinal(), new ColorSpace.Rgb(
"SMPTE RP 431-2-2007 DCI (P3)",
DCI_P3_PRIMARIES,
new float[] { 0.314f, 0.351f },
2.6,
0.0f, 1.0f,
Named.DCI_P3.ordinal()
- );
+ ));
sDataToColorSpaces.put(DataSpace.DATASPACE_DCI_P3, Named.DCI_P3.ordinal());
- sNamedColorSpaces[Named.DISPLAY_P3.ordinal()] = new ColorSpace.Rgb(
+ sNamedColorSpaceMap.put(Named.DISPLAY_P3.ordinal(), new ColorSpace.Rgb(
"Display P3",
DCI_P3_PRIMARIES,
ILLUMINANT_D65,
null,
SRGB_TRANSFER_PARAMETERS,
Named.DISPLAY_P3.ordinal()
- );
+ ));
sDataToColorSpaces.put(DataSpace.DATASPACE_DISPLAY_P3, Named.DISPLAY_P3.ordinal());
- sNamedColorSpaces[Named.NTSC_1953.ordinal()] = new ColorSpace.Rgb(
+ sNamedColorSpaceMap.put(Named.NTSC_1953.ordinal(), new ColorSpace.Rgb(
"NTSC (1953)",
NTSC_1953_PRIMARIES,
ILLUMINANT_C,
null,
SMPTE_170M_TRANSFER_PARAMETERS,
Named.NTSC_1953.ordinal()
- );
- sNamedColorSpaces[Named.SMPTE_C.ordinal()] = new ColorSpace.Rgb(
+ ));
+ sNamedColorSpaceMap.put(Named.SMPTE_C.ordinal(), new ColorSpace.Rgb(
"SMPTE-C RGB",
new float[] { 0.630f, 0.340f, 0.310f, 0.595f, 0.155f, 0.070f },
ILLUMINANT_D65,
null,
SMPTE_170M_TRANSFER_PARAMETERS,
Named.SMPTE_C.ordinal()
- );
- sNamedColorSpaces[Named.ADOBE_RGB.ordinal()] = new ColorSpace.Rgb(
+ ));
+ sNamedColorSpaceMap.put(Named.ADOBE_RGB.ordinal(), new ColorSpace.Rgb(
"Adobe RGB (1998)",
new float[] { 0.64f, 0.33f, 0.21f, 0.71f, 0.15f, 0.06f },
ILLUMINANT_D65,
2.2,
0.0f, 1.0f,
Named.ADOBE_RGB.ordinal()
- );
+ ));
sDataToColorSpaces.put(DataSpace.DATASPACE_ADOBE_RGB, Named.ADOBE_RGB.ordinal());
- sNamedColorSpaces[Named.PRO_PHOTO_RGB.ordinal()] = new ColorSpace.Rgb(
+ sNamedColorSpaceMap.put(Named.PRO_PHOTO_RGB.ordinal(), new ColorSpace.Rgb(
"ROMM RGB ISO 22028-2:2013",
new float[] { 0.7347f, 0.2653f, 0.1596f, 0.8404f, 0.0366f, 0.0001f },
ILLUMINANT_D50,
null,
new Rgb.TransferParameters(1.0, 0.0, 1 / 16.0, 0.031248, 1.8),
Named.PRO_PHOTO_RGB.ordinal()
- );
- sNamedColorSpaces[Named.ACES.ordinal()] = new ColorSpace.Rgb(
+ ));
+ sNamedColorSpaceMap.put(Named.ACES.ordinal(), new ColorSpace.Rgb(
"SMPTE ST 2065-1:2012 ACES",
new float[] { 0.73470f, 0.26530f, 0.0f, 1.0f, 0.00010f, -0.0770f },
ILLUMINANT_D60,
1.0,
-65504.0f, 65504.0f,
Named.ACES.ordinal()
- );
- sNamedColorSpaces[Named.ACESCG.ordinal()] = new ColorSpace.Rgb(
+ ));
+ sNamedColorSpaceMap.put(Named.ACESCG.ordinal(), new ColorSpace.Rgb(
"Academy S-2014-004 ACEScg",
new float[] { 0.713f, 0.293f, 0.165f, 0.830f, 0.128f, 0.044f },
ILLUMINANT_D60,
1.0,
-65504.0f, 65504.0f,
Named.ACESCG.ordinal()
- );
- sNamedColorSpaces[Named.CIE_XYZ.ordinal()] = new Xyz(
+ ));
+ sNamedColorSpaceMap.put(Named.CIE_XYZ.ordinal(), new Xyz(
"Generic XYZ",
Named.CIE_XYZ.ordinal()
- );
- sNamedColorSpaces[Named.CIE_LAB.ordinal()] = new ColorSpace.Lab(
+ ));
+ sNamedColorSpaceMap.put(Named.CIE_LAB.ordinal(), new ColorSpace.Lab(
"Generic L*a*b*",
Named.CIE_LAB.ordinal()
- );
- sNamedColorSpaces[Named.BT2020_HLG.ordinal()] = new ColorSpace.Rgb(
+ ));
+ sNamedColorSpaceMap.put(Named.BT2020_HLG.ordinal(), new ColorSpace.Rgb(
"Hybrid Log Gamma encoding",
BT2020_PRIMARIES,
ILLUMINANT_D65,
@@ -1668,9 +1701,9 @@
0.0f, 1.0f,
BT2020_HLG_TRANSFER_PARAMETERS,
Named.BT2020_HLG.ordinal()
- );
+ ));
sDataToColorSpaces.put(DataSpace.DATASPACE_BT2020_HLG, Named.BT2020_HLG.ordinal());
- sNamedColorSpaces[Named.BT2020_PQ.ordinal()] = new ColorSpace.Rgb(
+ sNamedColorSpaceMap.put(Named.BT2020_PQ.ordinal(), new ColorSpace.Rgb(
"Perceptual Quantizer encoding",
BT2020_PRIMARIES,
ILLUMINANT_D65,
@@ -1680,8 +1713,14 @@
0.0f, 1.0f,
BT2020_PQ_TRANSFER_PARAMETERS,
Named.BT2020_PQ.ordinal()
- );
+ ));
sDataToColorSpaces.put(DataSpace.DATASPACE_BT2020_PQ, Named.BT2020_PQ.ordinal());
+ if (Flags.okLabColorspace()) {
+ sNamedColorSpaceMap.put(Named.OK_LAB.ordinal(), new ColorSpace.OkLab(
+ "Oklab",
+ Named.OK_LAB.ordinal()
+ ));
+ }
}
private static double transferHLGOETF(Rgb.TransferParameters params, double x) {
@@ -2142,10 +2181,103 @@
return v;
}
+ }
- private static float clamp(float x, float min, float max) {
- return x < min ? min : x > max ? max : x;
+ private static float clamp(float x, float min, float max) {
+ return x < min ? min : x > max ? max : x;
+ }
+
+ /**
+ * Implementation of the Oklab color space. Oklab uses a D65 white point.
+ */
+ @AnyThread
+ private static final class OkLab extends ColorSpace {
+
+ private OkLab(@NonNull String name, @IntRange(from = MIN_ID, to = MAX_ID) int id) {
+ super(name, Model.LAB, id);
}
+
+ @Override
+ public boolean isWideGamut() {
+ return true;
+ }
+
+ @Override
+ public float getMinValue(@IntRange(from = 0, to = 3) int component) {
+ return component == 0 ? 0.0f : -0.5f;
+ }
+
+ @Override
+ public float getMaxValue(@IntRange(from = 0, to = 3) int component) {
+ return component == 0 ? 1.0f : 0.5f;
+ }
+
+ @Override
+ public float[] toXyz(@NonNull @Size(min = 3) float[] v) {
+ v[0] = clamp(v[0], 0.0f, 1.0f);
+ v[1] = clamp(v[1], -0.5f, 0.5f);
+ v[2] = clamp(v[2], -0.5f, 0.5f);
+
+ mul3x3Float3(INVERSE_M2, v);
+ v[0] = v[0] * v[0] * v[0];
+ v[1] = v[1] * v[1] * v[1];
+ v[2] = v[2] * v[2] * v[2];
+
+ mul3x3Float3(INVERSE_M1, v);
+
+ return v;
+ }
+
+ @Override
+ public float[] fromXyz(@NonNull @Size(min = 3) float[] v) {
+ mul3x3Float3(M1, v);
+
+ v[0] = (float) Math.cbrt(v[0]);
+ v[1] = (float) Math.cbrt(v[1]);
+ v[2] = (float) Math.cbrt(v[2]);
+
+ mul3x3Float3(M2, v);
+ return v;
+ }
+
+ /**
+ * Temp array used as input to compute M1 below
+ */
+ private static final float[] M1TMP = {
+ 0.8189330101f, 0.0329845436f, 0.0482003018f,
+ 0.3618667424f, 0.9293118715f, 0.2643662691f,
+ -0.1288597137f, 0.0361456387f, 0.6338517070f
+ };
+
+ /**
+ * This is the matrix applied before the nonlinear transform for (D50) XYZ-to-Oklab.
+ * This combines the D50-to-D65 white point transform with the normal transform matrix
+ * because this is always done together in [fromXyz].
+ */
+ private static final float[] M1 = mul3x3(
+ M1TMP,
+ chromaticAdaptation(Adaptation.BRADFORD, ILLUMINANT_D50, ILLUMINANT_D65)
+ );
+
+ /**
+ * Matrix applied after the nonlinear transform.
+ */
+ private static final float[] M2 = {
+ 0.2104542553f, 1.9779984951f, 0.0259040371f,
+ 0.7936177850f, -2.4285922050f, 0.7827717662f,
+ -0.0040720468f, 0.4505937099f, -0.8086757660f
+ };
+
+ /**
+ * The inverse of the [M1] matrix, transforming back to XYZ (D50)
+ */
+ private static final float[] INVERSE_M1 = inverse3x3(M1);
+
+ /**
+ * The inverse of the [M2] matrix, doing the first linear transform in the
+ * Oklab-to-XYZ before doing the nonlinear transform.
+ */
+ private static final float[] INVERSE_M2 = inverse3x3(M2);
}
/**
diff --git a/keystore/java/android/security/KeyStoreException.java b/keystore/java/android/security/KeyStoreException.java
index 5825fac..eea5690 100644
--- a/keystore/java/android/security/KeyStoreException.java
+++ b/keystore/java/android/security/KeyStoreException.java
@@ -679,6 +679,9 @@
sErrorCodeToFailureInfo.put(ResponseCode.OUT_OF_KEYS_REQUIRES_SYSTEM_UPGRADE,
new PublicErrorInformation(IS_SYSTEM_ERROR | IS_TRANSIENT_ERROR,
ERROR_DEVICE_REQUIRES_UPGRADE_FOR_ATTESTATION));
+ sErrorCodeToFailureInfo.put(ResponseCode.GET_ATTESTATION_APPLICATION_ID_FAILED,
+ new PublicErrorInformation(IS_SYSTEM_ERROR | IS_TRANSIENT_ERROR,
+ ERROR_INTERNAL_SYSTEM_ERROR));
sErrorCodeToFailureInfo.put(ResponseCode.OUT_OF_KEYS_PENDING_INTERNET_CONNECTIVITY,
new PublicErrorInformation(IS_SYSTEM_ERROR | IS_TRANSIENT_ERROR,
ERROR_ATTESTATION_KEYS_UNAVAILABLE));
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
index 16c77d0..ecf4720 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
@@ -24,6 +24,7 @@
import android.app.compat.CompatChanges;
import android.content.Context;
import android.hardware.devicestate.DeviceStateManager;
+import android.os.SystemProperties;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -50,6 +51,11 @@
private static final String TAG = "WindowExtensionsImpl";
/**
+ * The value of the system property that indicates no override is set.
+ */
+ private static final int NO_LEVEL_OVERRIDE = -1;
+
+ /**
* The min version of the WM Extensions that must be supported in the current platform version.
*/
@VisibleForTesting
@@ -66,14 +72,30 @@
WindowExtensionsImpl() {
mIsActivityEmbeddingEnabled = isActivityEmbeddingEnabled();
- Log.i(TAG, "Initializing Window Extensions, vendor API level=" + mVersion
- + ", activity embedding enabled=" + mIsActivityEmbeddingEnabled);
+
+ Log.i(TAG, generateLogMessage());
+ }
+
+ private String generateLogMessage() {
+ final StringBuilder logBuilder = new StringBuilder("Initializing Window Extensions, "
+ + "vendor API level=" + mVersion);
+ final int levelOverride = getLevelOverride();
+ if (levelOverride != NO_LEVEL_OVERRIDE) {
+ logBuilder.append(", override to ").append(levelOverride);
+ }
+ logBuilder.append(", activity embedding enabled=").append(mIsActivityEmbeddingEnabled);
+ return logBuilder.toString();
}
// TODO(b/241126279) Introduce constants to better version functionality
@Override
public int getVendorApiLevel() {
- return mVersion;
+ final int levelOverride = getLevelOverride();
+ return (levelOverride != NO_LEVEL_OVERRIDE) ? levelOverride : mVersion;
+ }
+
+ private int getLevelOverride() {
+ return SystemProperties.getInt("persist.wm.debug.ext_version_override", NO_LEVEL_OVERRIDE);
}
@NonNull
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
index 94c281f..290fefa 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
@@ -1394,10 +1394,16 @@
primaryBounds = mProperties.mIsReversedLayout ? boundsBottom : boundsTop;
secondaryBounds = mProperties.mIsReversedLayout ? boundsTop : boundsBottom;
}
- t.setWindowCrop(mPrimaryVeil, primaryBounds.width(), primaryBounds.height());
- t.setWindowCrop(mSecondaryVeil, secondaryBounds.width(), secondaryBounds.height());
- t.setPosition(mPrimaryVeil, primaryBounds.left, primaryBounds.top);
- t.setPosition(mSecondaryVeil, secondaryBounds.left, secondaryBounds.top);
+ if (mPrimaryVeil != null) {
+ t.setWindowCrop(mPrimaryVeil, primaryBounds.width(), primaryBounds.height());
+ t.setPosition(mPrimaryVeil, primaryBounds.left, primaryBounds.top);
+ t.setVisibility(mPrimaryVeil, !primaryBounds.isEmpty());
+ }
+ if (mSecondaryVeil != null) {
+ t.setWindowCrop(mSecondaryVeil, secondaryBounds.width(), secondaryBounds.height());
+ t.setPosition(mSecondaryVeil, secondaryBounds.left, secondaryBounds.top);
+ t.setVisibility(mSecondaryVeil, !secondaryBounds.isEmpty());
+ }
}
private static float[] colorToFloatArray(@NonNull Color color) {
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 15f8c32..112eb61 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -111,3 +111,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "animate_bubble_size_change"
+ namespace: "multitasking"
+ description: "Turns on the animation for bubble bar icons size change"
+ bug: "335575529"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index aafb2e1..150a6e6 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -84,7 +84,7 @@
<string name="notification_bubble_title" msgid="6082910224488253378">"Bublina"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovat"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina byla zavřena."</string>
- <string name="restart_button_description" msgid="4564728020654658478">"Klepnutím tuto aplikaci kvůli lepšímu zobrazení restartujete"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Klepnutím tuto aplikaci restartujete kvůli lepšímu zobrazení"</string>
<string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Změnit v Nastavení poměr stran této aplikace"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Změnit poměr stran"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problémy s fotoaparátem?\nKlepnutím vyřešíte"</string>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index 9f03d8b..6005be4 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -85,7 +85,7 @@
<string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen er avvist."</string>
<string name="restart_button_description" msgid="4564728020654658478">"Trykk for å starte denne appen på nytt og få en bedre visning"</string>
- <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Endre høyde/bredde-forholdet for denne appen i innstillingene"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Endre høyde/bredde-forholdet for denne appen i Innstillinger"</string>
<string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Endre høyde/bredde-forholdet"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Har du kameraproblemer?\nTrykk for å tilpasse"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ble ikke problemet løst?\nTrykk for å gå tilbake"</string>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index f27f46c..89cddc3 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -459,6 +459,12 @@
start of this area. -->
<dimen name="desktop_mode_customizable_caption_margin_end">152dp</dimen>
+ <!-- The default minimum allowed window width when resizing a window in desktop mode. -->
+ <dimen name="desktop_mode_minimum_window_width">386dp</dimen>
+
+ <!-- The default minimum allowed window height when resizing a window in desktop mode. -->
+ <dimen name="desktop_mode_minimum_window_height">352dp</dimen>
+
<!-- The width of the maximize menu in desktop mode. -->
<dimen name="desktop_mode_maximize_menu_width">228dp</dimen>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 0119289..0fd21f3 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
@@ -30,6 +30,7 @@
import android.content.Context;
import android.content.res.Configuration;
import android.database.ContentObserver;
+import android.graphics.Rect;
import android.hardware.input.InputManager;
import android.net.Uri;
import android.os.Bundle;
@@ -47,6 +48,7 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.RemoteAnimationTarget;
+import android.view.WindowManager;
import android.window.BackAnimationAdapter;
import android.window.BackEvent;
import android.window.BackMotionEvent;
@@ -119,6 +121,9 @@
private final ShellCommandHandler mShellCommandHandler;
private final ShellExecutor mShellExecutor;
private final Handler mBgHandler;
+ private final WindowManager mWindowManager;
+ @VisibleForTesting
+ final Rect mTouchableArea = new Rect();
/**
* Tracks the current user back gesture.
@@ -222,6 +227,8 @@
mShellBackAnimationRegistry = shellBackAnimationRegistry;
mLatencyTracker = LatencyTracker.getInstance(mContext);
mShellCommandHandler = shellCommandHandler;
+ mWindowManager = context.getSystemService(WindowManager.class);
+ updateTouchableArea();
}
private void onInit() {
@@ -283,6 +290,11 @@
@Override
public void onConfigurationChanged(Configuration newConfig) {
mShellBackAnimationRegistry.onConfigurationChanged(newConfig);
+ updateTouchableArea();
+ }
+
+ private void updateTouchableArea() {
+ mTouchableArea.set(mWindowManager.getCurrentWindowMetrics().getBounds());
}
@Override
@@ -416,11 +428,18 @@
if (!shouldDispatchToAnimator && mActiveCallback != null) {
mCurrentTracker.updateStartLocation();
tryDispatchOnBackStarted(mActiveCallback, mCurrentTracker.createStartEvent(null));
+ if (mBackNavigationInfo != null && !isAppProgressGenerationAllowed()) {
+ tryPilferPointers();
+ }
} else if (shouldDispatchToAnimator) {
tryPilferPointers();
}
}
+ private boolean isAppProgressGenerationAllowed() {
+ return mBackNavigationInfo.getTouchableRegion().equals(mTouchableArea);
+ }
+
/**
* Called when a new motion event needs to be transferred to this
* {@link BackAnimationController}
@@ -536,6 +555,9 @@
// App is handling back animation. Cancel system animation latency tracking.
cancelLatencyTracking();
tryDispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent(null));
+ if (!isAppProgressGenerationAllowed()) {
+ tryPilferPointers();
+ }
}
}
@@ -642,7 +664,8 @@
private void dispatchOnBackProgressed(IOnBackInvokedCallback callback,
BackMotionEvent backEvent) {
- if (callback == null || !shouldDispatchToAnimator()) {
+ if (callback == null || (!shouldDispatchToAnimator() && mBackNavigationInfo != null
+ && isAppProgressGenerationAllowed())) {
return;
}
try {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 32873d9..81e7d1f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -602,7 +602,7 @@
List<Bubble> removedBubbles = filterAllBubbles(bubble ->
userId == bubble.getUser().getIdentifier());
for (Bubble b : removedBubbles) {
- doRemove(b.getKey(), Bubbles.DISMISS_USER_REMOVED);
+ doRemove(b.getKey(), Bubbles.DISMISS_USER_ACCOUNT_REMOVED);
}
if (!removedBubbles.isEmpty()) {
dispatchPendingChanges();
@@ -678,7 +678,7 @@
|| reason == Bubbles.DISMISS_SHORTCUT_REMOVED
|| reason == Bubbles.DISMISS_PACKAGE_REMOVED
|| reason == Bubbles.DISMISS_USER_CHANGED
- || reason == Bubbles.DISMISS_USER_REMOVED;
+ || reason == Bubbles.DISMISS_USER_ACCOUNT_REMOVED;
int indexToRemove = indexForKey(key);
if (indexToRemove == -1) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 1d053f9..82af88d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -61,7 +61,7 @@
DISMISS_NOTIF_CANCEL, DISMISS_ACCESSIBILITY_ACTION, DISMISS_NO_LONGER_BUBBLE,
DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT,
DISMISS_OVERFLOW_MAX_REACHED, DISMISS_SHORTCUT_REMOVED, DISMISS_PACKAGE_REMOVED,
- DISMISS_NO_BUBBLE_UP, DISMISS_RELOAD_FROM_DISK, DISMISS_USER_REMOVED,
+ DISMISS_NO_BUBBLE_UP, DISMISS_RELOAD_FROM_DISK, DISMISS_USER_ACCOUNT_REMOVED,
DISMISS_SWITCH_TO_STACK})
@Target({FIELD, LOCAL_VARIABLE, PARAMETER})
@interface DismissReason {
@@ -82,7 +82,7 @@
int DISMISS_PACKAGE_REMOVED = 13;
int DISMISS_NO_BUBBLE_UP = 14;
int DISMISS_RELOAD_FROM_DISK = 15;
- int DISMISS_USER_REMOVED = 16;
+ int DISMISS_USER_ACCOUNT_REMOVED = 16;
int DISMISS_SWITCH_TO_STACK = 17;
/** Returns a binder that can be passed to an external process to manipulate Bubbles. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
index a7da07d..972dce5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
@@ -209,7 +209,7 @@
@Override
public void onDismissBubble(Bubble bubble) {
- mManager.dismissBubble(bubble, Bubbles.DISMISS_USER_REMOVED);
+ mManager.dismissBubble(bubble, Bubbles.DISMISS_USER_GESTURE);
}
});
mHandleView.setOnClickListener(view -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index 57e95d6..f4ac5f2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -538,8 +538,10 @@
@Override
public void onAnimationStart(Animator animation) {
+ ValueAnimator valueAnimator = (ValueAnimator) animation;
+ float value = (float) valueAnimator.getAnimatedValue();
SurfaceControl.Transaction t = mTransactionPool.acquire();
- t.setPosition(mImeSourceControl.getLeash(), x, startY);
+ t.setPosition(mImeSourceControl.getLeash(), x, value);
if (DEBUG) {
Slog.d(TAG, "onAnimationStart d:" + mDisplayId + " top:"
+ imeTop(hiddenY) + "->" + imeTop(shownY)
@@ -549,7 +551,7 @@
imeTop(shownY), mAnimationDirection == DIRECTION_SHOW, isFloating, t);
mAnimateAlpha = (flags & ImePositionProcessor.IME_ANIMATION_NO_ALPHA) == 0;
final float alpha = (mAnimateAlpha || isFloating)
- ? (startY - hiddenY) / (shownY - hiddenY)
+ ? (value - hiddenY) / (shownY - hiddenY)
: 1.f;
t.setAlpha(mImeSourceControl.getLeash(), alpha);
if (mAnimationDirection == DIRECTION_SHOW) {
@@ -560,7 +562,7 @@
if (DEBUG_IME_VISIBILITY) {
EventLog.writeEvent(IMF_IME_REMOTE_ANIM_START,
mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE,
- mDisplayId, mAnimationDirection, alpha, startY , endY,
+ mDisplayId, mAnimationDirection, alpha, value, endY,
Objects.toString(mImeSourceControl.getLeash()),
Objects.toString(mImeSourceControl.getInsetsHint()),
Objects.toString(mImeSourceControl.getSurfacePosition()),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl
index b5f25433f..e779879 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl
@@ -53,9 +53,11 @@
* @param destinationBounds the destination bounds the PiP window lands into
* @param overlay an optional overlay to fade out after entering PiP
* @param appBounds the bounds used to set the buffer size of the optional content overlay
+ * @param sourceRectHint the bounds to show in the transition to PiP
*/
oneway void stopSwipePipToHome(int taskId, in ComponentName componentName,
- in Rect destinationBounds, in SurfaceControl overlay, in Rect appBounds) = 2;
+ in Rect destinationBounds, in SurfaceControl overlay, in Rect appBounds,
+ in Rect sourceRectHint) = 2;
/**
* Notifies the swiping Activity to PiP onto home transition is aborted
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
index 579a794..a09720d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
@@ -22,6 +22,7 @@
import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageManager
+import android.graphics.Rect
import android.os.RemoteException
import android.os.SystemProperties
import android.util.DisplayMetrics
@@ -138,6 +139,30 @@
}
}
+
+ /**
+ * Returns a fake source rect hint for animation purposes when app-provided one is invalid.
+ * Resulting adjusted source rect hint lets the app icon in the content overlay to stay visible.
+ */
+ @JvmStatic
+ fun getEnterPipWithOverlaySrcRectHint(appBounds: Rect, aspectRatio: Float): Rect {
+ val appBoundsAspRatio = appBounds.width().toFloat() / appBounds.height()
+ val width: Int
+ val height: Int
+ var left = 0
+ var top = 0
+ if (appBoundsAspRatio < aspectRatio) {
+ width = appBounds.width()
+ height = Math.round(width / aspectRatio)
+ top = (appBounds.height() - height) / 2
+ } else {
+ height = appBounds.height()
+ width = Math.round(height * aspectRatio)
+ left = (appBounds.width() - width) / 2
+ }
+ return Rect(left, top, left + width, top + height)
+ }
+
private var isPip2ExperimentEnabled: Boolean? = null
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index 2234041..c2242a8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -336,6 +336,11 @@
setTouching();
mStartPos = touchPos;
mMoving = false;
+ // This triggers initialization of things like the resize veil in preparation for
+ // showing it when the user moves the divider past the slop, and has to be done
+ // before onStartDragging() which starts the jank interaction tracing
+ mSplitLayout.updateDividerBounds(mSplitLayout.getDividerPosition(),
+ false /* shouldUseParallaxEffect */);
mSplitLayout.onStartDragging();
break;
case MotionEvent.ACTION_MOVE:
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java
index 835f1af..07082a5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java
@@ -53,7 +53,7 @@
private final ShellExecutor mMainExecutor;
- private boolean mIsActivityLetterboxed;
+ private boolean mIsLetterboxDoubleTapEnabled;
private int mLetterboxVerticalPosition;
@@ -91,7 +91,7 @@
Function<Integer, Integer> disappearTimeSupplier) {
super(context, taskInfo, syncQueue, taskListener, displayLayout);
final AppCompatTaskInfo appCompatTaskInfo = taskInfo.appCompatTaskInfo;
- mIsActivityLetterboxed = appCompatTaskInfo.isLetterboxDoubleTapEnabled;
+ mIsLetterboxDoubleTapEnabled = appCompatTaskInfo.isLetterboxDoubleTapEnabled;
mLetterboxVerticalPosition = appCompatTaskInfo.topActivityLetterboxVerticalPosition;
mLetterboxHorizontalPosition = appCompatTaskInfo.topActivityLetterboxHorizontalPosition;
mTopActivityLetterboxWidth = appCompatTaskInfo.topActivityLetterboxWidth;
@@ -119,7 +119,7 @@
@Override
protected boolean eligibleToShowLayout() {
- return mIsActivityLetterboxed
+ return mIsLetterboxDoubleTapEnabled
&& (mLetterboxVerticalPosition != -1 || mLetterboxHorizontalPosition != -1);
}
@@ -142,13 +142,13 @@
@Override
public boolean updateCompatInfo(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener,
boolean canShow) {
- final boolean prevIsActivityLetterboxed = mIsActivityLetterboxed;
+ final boolean prevIsLetterboxDoubleTapEnabled = mIsLetterboxDoubleTapEnabled;
final int prevLetterboxVerticalPosition = mLetterboxVerticalPosition;
final int prevLetterboxHorizontalPosition = mLetterboxHorizontalPosition;
final int prevTopActivityLetterboxWidth = mTopActivityLetterboxWidth;
final int prevTopActivityLetterboxHeight = mTopActivityLetterboxHeight;
final AppCompatTaskInfo appCompatTaskInfo = taskInfo.appCompatTaskInfo;
- mIsActivityLetterboxed = appCompatTaskInfo.isLetterboxDoubleTapEnabled;
+ mIsLetterboxDoubleTapEnabled = appCompatTaskInfo.isLetterboxDoubleTapEnabled;
mLetterboxVerticalPosition = appCompatTaskInfo.topActivityLetterboxVerticalPosition;
mLetterboxHorizontalPosition = appCompatTaskInfo.topActivityLetterboxHorizontalPosition;
mTopActivityLetterboxWidth = appCompatTaskInfo.topActivityLetterboxWidth;
@@ -162,7 +162,7 @@
mHasLetterboxSizeChanged = prevTopActivityLetterboxWidth != mTopActivityLetterboxWidth
|| prevTopActivityLetterboxHeight != mTopActivityLetterboxHeight;
- if (mHasUserDoubleTapped || prevIsActivityLetterboxed != mIsActivityLetterboxed
+ if (mHasUserDoubleTapped || prevIsLetterboxDoubleTapEnabled != mIsLetterboxDoubleTapEnabled
|| prevLetterboxVerticalPosition != mLetterboxVerticalPosition
|| prevLetterboxHorizontalPosition != mLetterboxHorizontalPosition
|| prevTopActivityLetterboxWidth != mTopActivityLetterboxWidth
@@ -249,7 +249,7 @@
&& (mLetterboxVerticalPosition == REACHABILITY_LEFT_OR_UP_POSITION
|| mLetterboxVerticalPosition == REACHABILITY_RIGHT_OR_BOTTOM_POSITION));
- if (mIsActivityLetterboxed && (eligibleForDisplayHorizontalEducation
+ if (mIsLetterboxDoubleTapEnabled && (eligibleForDisplayHorizontalEducation
|| eligibleForDisplayVerticalEducation)) {
int availableWidth = getTaskBounds().width() - mTopActivityLetterboxWidth;
int availableHeight = getTaskBounds().height() - mTopActivityLetterboxHeight;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 991fbaf..609e5af 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -87,6 +87,7 @@
import com.android.wm.shell.recents.RecentTasks;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.recents.RecentsTransitionHandler;
+import com.android.wm.shell.recents.TaskStackTransitionObserver;
import com.android.wm.shell.shared.DesktopModeStatus;
import com.android.wm.shell.shared.ShellTransitions;
import com.android.wm.shell.shared.annotations.ShellAnimationThread;
@@ -619,12 +620,13 @@
TaskStackListenerImpl taskStackListener,
ActivityTaskManager activityTaskManager,
Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
+ TaskStackTransitionObserver taskStackTransitionObserver,
@ShellMainThread ShellExecutor mainExecutor
) {
return Optional.ofNullable(
RecentTasksController.create(context, shellInit, shellController,
shellCommandHandler, taskStackListener, activityTaskManager,
- desktopModeTaskRepository, mainExecutor));
+ desktopModeTaskRepository, taskStackTransitionObserver, mainExecutor));
}
@BindsOptionalOf
@@ -924,6 +926,19 @@
}
//
+ // Task Stack
+ //
+
+ @WMSingleton
+ @Provides
+ static TaskStackTransitionObserver provideTaskStackTransitionObserver(
+ Lazy<Transitions> transitions,
+ ShellInit shellInit
+ ) {
+ return new TaskStackTransitionObserver(transitions, shellInit);
+ }
+
+ //
// Misc
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 12bbd51..87bd840 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -121,9 +121,9 @@
*/
@Module(
includes = {
- WMShellBaseModule.class,
- PipModule.class,
- ShellBackAnimationModule.class,
+ WMShellBaseModule.class,
+ PipModule.class,
+ ShellBackAnimationModule.class,
})
public abstract class WMShellModule {
@@ -664,7 +664,8 @@
@Provides
static Object provideIndependentShellComponentsToCreate(
DragAndDropController dragAndDropController,
- Optional<DesktopTasksTransitionObserver> desktopTasksTransitionObserverOptional) {
+ Optional<DesktopTasksTransitionObserver> desktopTasksTransitionObserverOptional
+ ) {
return new Object();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
index 109868d..9192e6e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
@@ -187,7 +187,10 @@
KEYBOARD_SHORTCUT_ENTER(
FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__KEYBOARD_SHORTCUT_ENTER
),
- SCREEN_ON(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__SCREEN_ON)
+ SCREEN_ON(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__SCREEN_ON),
+ APP_FROM_OVERVIEW(
+ FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__APP_FROM_OVERVIEW
+ ),
}
/**
@@ -204,7 +207,7 @@
FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__KEYBOARD_SHORTCUT_EXIT
),
RETURN_HOME_OR_OVERVIEW(
- FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME
+ FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__RETURN_HOME_OR_OVERVIEW
),
TASK_FINISHED(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__TASK_FINISHED),
SCREEN_OFF(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__SCREEN_OFF)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
index 075e3ae..cee2d92 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
@@ -314,8 +314,7 @@
WindowManager.TRANSIT_WAKE -> EnterReason.SCREEN_ON
Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP -> EnterReason.APP_HANDLE_DRAG
TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON -> EnterReason.APP_HANDLE_MENU_BUTTON
- // TODO(b/344822506): Create and update EnterReason to APP_FROM_OVERVIEW
- TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW -> EnterReason.UNKNOWN_ENTER
+ TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW -> EnterReason.APP_FROM_OVERVIEW
TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT -> EnterReason.KEYBOARD_SHORTCUT_ENTER
WindowManager.TRANSIT_OPEN -> EnterReason.APP_FREEFORM_INTENT
else -> EnterReason.UNKNOWN_ENTER
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index 57c0732..0a3c15b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -40,6 +40,7 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.animation.Interpolators;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.transition.Transitions;
@@ -619,19 +620,8 @@
// This is done for entering case only.
if (isInPipDirection(direction)) {
final float aspectRatio = endValue.width() / (float) endValue.height();
- if ((startValue.width() / (float) startValue.height()) > aspectRatio) {
- // use the full height.
- adjustedSourceRectHint.set(0, 0,
- (int) (startValue.height() * aspectRatio), startValue.height());
- adjustedSourceRectHint.offset(
- (startValue.width() - adjustedSourceRectHint.width()) / 2, 0);
- } else {
- // use the full width.
- adjustedSourceRectHint.set(0, 0,
- startValue.width(), (int) (startValue.width() / aspectRatio));
- adjustedSourceRectHint.offset(
- 0, (startValue.height() - adjustedSourceRectHint.height()) / 2);
- }
+ adjustedSourceRectHint.set(PipUtils.getEnterPipWithOverlaySrcRectHint(
+ startValue, aspectRatio));
}
} else {
adjustedSourceRectHint.set(sourceRectHint);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 04dd0ef..e4420d7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -373,6 +373,10 @@
@NonNull
final Rect mAppBounds = new Rect();
+ /** The source rect hint from stopSwipePipToHome(). */
+ @Nullable
+ private Rect mSwipeSourceRectHint;
+
public PipTaskOrganizer(Context context,
@NonNull SyncTransactionQueue syncTransactionQueue,
@NonNull PipTransitionState pipTransitionState,
@@ -504,7 +508,7 @@
* Expect {@link #onTaskAppeared(ActivityManager.RunningTaskInfo, SurfaceControl)} afterwards.
*/
public void stopSwipePipToHome(int taskId, ComponentName componentName, Rect destinationBounds,
- SurfaceControl overlay, Rect appBounds) {
+ SurfaceControl overlay, Rect appBounds, Rect sourceRectHint) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"stopSwipePipToHome: %s, stat=%s", componentName, mPipTransitionState);
// do nothing if there is no startSwipePipToHome being called before
@@ -513,6 +517,7 @@
}
mPipBoundsState.setBounds(destinationBounds);
setContentOverlay(overlay, appBounds);
+ mSwipeSourceRectHint = sourceRectHint;
if (ENABLE_SHELL_TRANSITIONS && overlay != null) {
// With Shell transition, the overlay was attached to the remote transition leash, which
// will be removed when the current transition is finished, so we need to reparent it
@@ -529,6 +534,20 @@
}
}
+ /**
+ * Returns non-null Rect if the pip is entering from swipe-to-home with a specified source hint.
+ * This also consumes the rect hint.
+ */
+ @Nullable
+ Rect takeSwipeSourceRectHint() {
+ final Rect sourceRectHint = mSwipeSourceRectHint;
+ if (sourceRectHint == null || sourceRectHint.isEmpty()) {
+ return null;
+ }
+ mSwipeSourceRectHint = null;
+ return mPipTransitionState.getInSwipePipToHomeTransition() ? sourceRectHint : null;
+ }
+
private void mayRemoveContentOverlay(SurfaceControl overlay) {
final WeakReference<SurfaceControl> overlayRef = new WeakReference<>(overlay);
final long timeoutDuration = (mEnterAnimationDuration
@@ -980,7 +999,6 @@
private void onEndOfSwipePipToHomeTransition() {
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- mPipTransitionController.setEnterAnimationType(ANIM_TYPE_BOUNDS);
return;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 5ee6f6b..3cae72d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -1004,8 +1004,11 @@
final Rect currentBounds = pipChange.getStartAbsBounds();
int rotationDelta = deltaRotation(startRotation, endRotation);
- Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect(
- taskInfo.pictureInPictureParams, currentBounds, destinationBounds);
+ Rect sourceHintRect = mPipOrganizer.takeSwipeSourceRectHint();
+ if (sourceHintRect == null) {
+ sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect(
+ taskInfo.pictureInPictureParams, currentBounds, destinationBounds);
+ }
if (rotationDelta != Surface.ROTATION_0
&& endRotation != mPipDisplayLayoutState.getRotation()) {
// Computes the destination bounds in new rotation.
@@ -1080,6 +1083,8 @@
mSurfaceTransactionHelper
.crop(finishTransaction, leash, destinationBounds)
.round(finishTransaction, leash, true /* applyCornerRadius */);
+ // Always reset to bounds animation type afterwards.
+ setEnterAnimationType(ANIM_TYPE_BOUNDS);
} else {
throw new RuntimeException("Unrecognized animation type: " + enterAnimationType);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index de105c0..8c4bf76 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -1001,9 +1001,9 @@
}
private void stopSwipePipToHome(int taskId, ComponentName componentName, Rect destinationBounds,
- SurfaceControl overlay, Rect appBounds) {
+ SurfaceControl overlay, Rect appBounds, Rect sourceRectHint) {
mPipTaskOrganizer.stopSwipePipToHome(taskId, componentName, destinationBounds, overlay,
- appBounds);
+ appBounds, sourceRectHint);
}
private void abortSwipePipToHome(int taskId, ComponentName componentName) {
@@ -1291,13 +1291,15 @@
@Override
public void stopSwipePipToHome(int taskId, ComponentName componentName,
- Rect destinationBounds, SurfaceControl overlay, Rect appBounds) {
+ Rect destinationBounds, SurfaceControl overlay, Rect appBounds,
+ Rect sourceRectHint) {
if (overlay != null) {
overlay.setUnreleasedWarningCallSite("PipController.stopSwipePipToHome");
}
executeRemoteCallWithTaskPermission(mController, "stopSwipePipToHome",
(controller) -> controller.stopSwipePipToHome(
- taskId, componentName, destinationBounds, overlay, appBounds));
+ taskId, componentName, destinationBounds, overlay, appBounds,
+ sourceRectHint));
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java
index 3e298e5..895c2ae 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java
@@ -19,11 +19,13 @@
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
+import android.content.Context;
import android.view.SurfaceControl;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.wm.shell.R;
import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import java.lang.annotation.Retention;
@@ -45,6 +47,7 @@
public static final int FADE_IN = 0;
public static final int FADE_OUT = 1;
+ private final int mEnterAnimationDuration;
private final SurfaceControl mLeash;
private final SurfaceControl.Transaction mStartTransaction;
@@ -55,7 +58,8 @@
private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
- public PipAlphaAnimator(SurfaceControl leash,
+ public PipAlphaAnimator(Context context,
+ SurfaceControl leash,
SurfaceControl.Transaction tx,
@Fade int direction) {
mLeash = leash;
@@ -67,6 +71,9 @@
}
mSurfaceControlTransactionFactory =
new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
+ mEnterAnimationDuration = context.getResources()
+ .getInteger(R.integer.config_pipEnterAnimationDuration);
+ setDuration(mEnterAnimationDuration);
addListener(this);
addUpdateListener(this);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
index 1846720..fc0d36d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
@@ -285,7 +285,8 @@
}
private void onSwipePipToHomeAnimationStart(int taskId, ComponentName componentName,
- Rect destinationBounds, SurfaceControl overlay, Rect appBounds) {
+ Rect destinationBounds, SurfaceControl overlay, Rect appBounds,
+ Rect sourceRectHint) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"onSwipePipToHomeAnimationStart: %s", componentName);
Bundle extra = new Bundle();
@@ -409,13 +410,15 @@
@Override
public void stopSwipePipToHome(int taskId, ComponentName componentName,
- Rect destinationBounds, SurfaceControl overlay, Rect appBounds) {
+ Rect destinationBounds, SurfaceControl overlay, Rect appBounds,
+ Rect sourceRectHint) {
if (overlay != null) {
overlay.setUnreleasedWarningCallSite("PipController.stopSwipePipToHome");
}
executeRemoteCallWithTaskPermission(mController, "stopSwipePipToHome",
(controller) -> controller.onSwipePipToHomeAnimationStart(
- taskId, componentName, destinationBounds, overlay, appBounds));
+ taskId, componentName, destinationBounds, overlay, appBounds,
+ sourceRectHint));
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 3e215d9..0b2db6c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -293,37 +293,32 @@
return false;
}
+ SurfaceControl overlayLeash = mPipTransitionState.getSwipePipToHomeOverlay();
PictureInPictureParams params = pipChange.getTaskInfo().pictureInPictureParams;
- Rect srcRectHint = params.getSourceRectHint();
- Rect startBounds = pipChange.getStartAbsBounds();
+
+ Rect appBounds = mPipTransitionState.getSwipePipToHomeAppBounds();
Rect destinationBounds = pipChange.getEndAbsBounds();
+ float aspectRatio = pipChange.getTaskInfo().pictureInPictureParams.getAspectRatioFloat();
+
+ // We fake the source rect hint when the one prvided by the app is invalid for
+ // the animation with an app icon overlay.
+ Rect animationSrcRectHint = overlayLeash == null ? params.getSourceRectHint()
+ : PipUtils.getEnterPipWithOverlaySrcRectHint(appBounds, aspectRatio);
+
WindowContainerTransaction finishWct = new WindowContainerTransaction();
SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
- if (PipBoundsAlgorithm.isSourceRectHintValidForEnterPip(srcRectHint, destinationBounds)) {
- final float scale = (float) destinationBounds.width() / srcRectHint.width();
- startTransaction.setWindowCrop(pipLeash, srcRectHint);
- startTransaction.setPosition(pipLeash,
- destinationBounds.left - srcRectHint.left * scale,
- destinationBounds.top - srcRectHint.top * scale);
+ final float scale = (float) destinationBounds.width() / animationSrcRectHint.width();
+ startTransaction.setWindowCrop(pipLeash, animationSrcRectHint);
+ startTransaction.setPosition(pipLeash,
+ destinationBounds.left - animationSrcRectHint.left * scale,
+ destinationBounds.top - animationSrcRectHint.top * scale);
+ startTransaction.setScale(pipLeash, scale, scale);
- // Reset the scale in case we are in the multi-activity case.
- // TO_FRONT transition already scales down the task in single-activity case, but
- // in multi-activity case, reparenting yields new reset scales coming from pinned task.
- startTransaction.setScale(pipLeash, scale, scale);
- } else {
- final float scaleX = (float) destinationBounds.width() / startBounds.width();
- final float scaleY = (float) destinationBounds.height() / startBounds.height();
+ if (overlayLeash != null) {
final int overlaySize = PipContentOverlay.PipAppIconOverlay.getOverlaySize(
mPipTransitionState.getSwipePipToHomeAppBounds(), destinationBounds);
- SurfaceControl overlayLeash = mPipTransitionState.getSwipePipToHomeOverlay();
-
- startTransaction.setPosition(pipLeash, destinationBounds.left, destinationBounds.top)
- .setScale(pipLeash, scaleX, scaleY)
- .setWindowCrop(pipLeash, startBounds)
- .reparent(overlayLeash, pipLeash)
- .setLayer(overlayLeash, Integer.MAX_VALUE);
// Overlay needs to be adjusted once a new draw comes in resetting surface transform.
tx.setScale(overlayLeash, 1f, 1f);
@@ -390,15 +385,23 @@
if (pipChange == null) {
return false;
}
- // cache the PiP task token and leash
- WindowContainerToken pipTaskToken = pipChange.getContainer();
- Preconditions.checkNotNull(mPipLeash, "Leash is null for alpha transition.");
- // start transition with 0 alpha
- startTransaction.setAlpha(mPipLeash, 0f);
- PipAlphaAnimator animator = new PipAlphaAnimator(mPipLeash,
- startTransaction, PipAlphaAnimator.FADE_IN);
- animator.setAnimationEndCallback(() -> finishCallback.onTransitionFinished(null));
+ Rect destinationBounds = pipChange.getEndAbsBounds();
+ SurfaceControl pipLeash = mPipTransitionState.mPinnedTaskLeash;
+ Preconditions.checkNotNull(pipLeash, "Leash is null for alpha transition.");
+
+ // Start transition with 0 alpha at the entry bounds.
+ startTransaction.setPosition(pipLeash, destinationBounds.left, destinationBounds.top)
+ .setWindowCrop(pipLeash, destinationBounds.width(), destinationBounds.height())
+ .setAlpha(pipLeash, 0f);
+
+ PipAlphaAnimator animator = new PipAlphaAnimator(mContext, pipLeash, startTransaction,
+ PipAlphaAnimator.FADE_IN);
+ animator.setAnimationEndCallback(() -> {
+ finishCallback.onTransitionFinished(null);
+ // This should update the pip transition state accordingly after we stop playing.
+ onClientDrawAtTransitionEnd();
+ });
animator.start();
return true;
@@ -480,10 +483,10 @@
private boolean isLegacyEnter(@NonNull TransitionInfo info) {
TransitionInfo.Change pipChange = getPipChange(info);
- // If the only change in the changes list is a TO_FRONT mode PiP task,
+ // If the only change in the changes list is a opening type PiP task,
// then this is legacy-enter PiP.
- return pipChange != null && pipChange.getMode() == TRANSIT_TO_FRONT
- && info.getChanges().size() == 1;
+ return pipChange != null && info.getChanges().size() == 1
+ && (pipChange.getMode() == TRANSIT_TO_FRONT || pipChange.getMode() == TRANSIT_OPEN);
}
private boolean isRemovePipTransition(@NonNull TransitionInfo info) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl
index 62d195e..245829e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl
@@ -42,4 +42,7 @@
* Called when a running task changes.
*/
void onRunningTaskChanged(in RunningTaskInfo taskInfo);
-}
+
+ /** A task has moved to front. */
+ oneway void onTaskMovedToFront(in RunningTaskInfo taskInfo);
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/OWNERS
new file mode 100644
index 0000000..452644b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/OWNERS
@@ -0,0 +1,6 @@
+# WM shell sub-module task stack owners
+uysalorhan@google.com
+samcackett@google.com
+alexchau@google.com
+silvajordan@google.com
+uwaisashraf@google.com
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 9d16246..03c8cf8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -20,6 +20,7 @@
import static android.content.pm.PackageManager.FEATURE_PC;
import static com.android.window.flags.Flags.enableDesktopWindowingTaskbarRunningApps;
+import static com.android.window.flags.Flags.enableTaskStackObserverInShell;
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_RECENT_TASKS;
import android.app.ActivityManager;
@@ -57,6 +58,7 @@
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
import com.android.wm.shell.util.SplitBounds;
@@ -73,7 +75,8 @@
* Manages the recent task list from the system, caching it as necessary.
*/
public class RecentTasksController implements TaskStackListenerCallback,
- RemoteCallable<RecentTasksController>, DesktopModeTaskRepository.ActiveTasksListener {
+ RemoteCallable<RecentTasksController>, DesktopModeTaskRepository.ActiveTasksListener,
+ TaskStackTransitionObserver.TaskStackTransitionObserverListener {
private static final String TAG = RecentTasksController.class.getSimpleName();
private final Context mContext;
@@ -84,6 +87,7 @@
private final TaskStackListenerImpl mTaskStackListener;
private final RecentTasksImpl mImpl = new RecentTasksImpl();
private final ActivityTaskManager mActivityTaskManager;
+ private final TaskStackTransitionObserver mTaskStackTransitionObserver;
private RecentsTransitionHandler mTransitionHandler = null;
private IRecentTasksListener mListener;
private final boolean mPcFeatureEnabled;
@@ -112,13 +116,15 @@
TaskStackListenerImpl taskStackListener,
ActivityTaskManager activityTaskManager,
Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
+ TaskStackTransitionObserver taskStackTransitionObserver,
@ShellMainThread ShellExecutor mainExecutor
) {
if (!context.getResources().getBoolean(com.android.internal.R.bool.config_hasRecents)) {
return null;
}
return new RecentTasksController(context, shellInit, shellController, shellCommandHandler,
- taskStackListener, activityTaskManager, desktopModeTaskRepository, mainExecutor);
+ taskStackListener, activityTaskManager, desktopModeTaskRepository,
+ taskStackTransitionObserver, mainExecutor);
}
RecentTasksController(Context context,
@@ -128,6 +134,7 @@
TaskStackListenerImpl taskStackListener,
ActivityTaskManager activityTaskManager,
Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
+ TaskStackTransitionObserver taskStackTransitionObserver,
ShellExecutor mainExecutor) {
mContext = context;
mShellController = shellController;
@@ -136,6 +143,7 @@
mPcFeatureEnabled = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
mTaskStackListener = taskStackListener;
mDesktopModeTaskRepository = desktopModeTaskRepository;
+ mTaskStackTransitionObserver = taskStackTransitionObserver;
mMainExecutor = mainExecutor;
shellInit.addInitCallback(this::onInit, this);
}
@@ -154,6 +162,10 @@
mShellCommandHandler.addDumpCallback(this::dump, this);
mTaskStackListener.addListener(this);
mDesktopModeTaskRepository.ifPresent(it -> it.addActiveTaskListener(this));
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ mTaskStackTransitionObserver.addTaskStackTransitionObserverListener(this,
+ mMainExecutor);
+ }
}
void setTransitionHandler(RecentsTransitionHandler handler) {
@@ -267,6 +279,12 @@
notifyRecentTasksChanged();
}
+ @Override
+ public void onTaskMovedToFrontThroughTransition(
+ ActivityManager.RunningTaskInfo runningTaskInfo) {
+ notifyTaskMovedToFront(runningTaskInfo);
+ }
+
@VisibleForTesting
void notifyRecentTasksChanged() {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENT_TASKS, "Notify recent tasks changed");
@@ -328,6 +346,19 @@
}
}
+ private void notifyTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
+ if (mListener == null
+ || !enableTaskStackObserverInShell()
+ || taskInfo.realActivity == null) {
+ return;
+ }
+ try {
+ mListener.onTaskMovedToFront(taskInfo);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed call onTaskMovedToFront", e);
+ }
+ }
+
private boolean shouldEnableRunningTasksForDesktopMode() {
return mPcFeatureEnabled
|| (DesktopModeStatus.canEnterDesktopMode(mContext)
@@ -464,6 +495,7 @@
}
return null;
}
+
public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.println(prefix + TAG);
@@ -547,6 +579,11 @@
public void onRunningTaskChanged(ActivityManager.RunningTaskInfo taskInfo) {
mListener.call(l -> l.onRunningTaskChanged(taskInfo));
}
+
+ @Override
+ public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
+ mListener.call(l -> l.onTaskMovedToFront(taskInfo));
+ }
};
public IRecentTasksImpl(RecentTasksController controller) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt
new file mode 100644
index 0000000..7c5f10a
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.recents
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.os.IBinder
+import android.util.ArrayMap
+import android.view.SurfaceControl
+import android.view.WindowManager
+import android.window.TransitionInfo
+import com.android.window.flags.Flags.enableTaskStackObserverInShell
+import com.android.wm.shell.shared.TransitionUtil
+import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.transition.Transitions
+import dagger.Lazy
+import java.util.concurrent.Executor
+
+/**
+ * A [Transitions.TransitionObserver] that observes shell transitions and sends updates to listeners
+ * about task stack changes.
+ *
+ * TODO(346588978) Move split/pip signals here as well so that launcher don't need to handle it
+ */
+class TaskStackTransitionObserver(
+ private val transitions: Lazy<Transitions>,
+ shellInit: ShellInit
+) : Transitions.TransitionObserver {
+
+ private val transitionToTransitionChanges: MutableMap<IBinder, TransitionChanges> =
+ mutableMapOf()
+ private val taskStackTransitionObserverListeners =
+ ArrayMap<TaskStackTransitionObserverListener, Executor>()
+
+ init {
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ shellInit.addInitCallback(::onInit, this)
+ }
+ }
+
+ fun onInit() {
+ transitions.get().registerObserver(this)
+ }
+
+ override fun onTransitionReady(
+ transition: IBinder,
+ info: TransitionInfo,
+ startTransaction: SurfaceControl.Transaction,
+ finishTransaction: SurfaceControl.Transaction
+ ) {
+ if (enableTaskStackObserverInShell()) {
+ val taskInfoList = mutableListOf<RunningTaskInfo>()
+ val transitionTypeList = mutableListOf<Int>()
+
+ for (change in info.changes) {
+ if (change.flags and TransitionInfo.FLAG_IS_WALLPAPER != 0) {
+ continue
+ }
+
+ val taskInfo = change.taskInfo
+ if (taskInfo == null || taskInfo.taskId == -1) {
+ continue
+ }
+
+ if (change.mode == WindowManager.TRANSIT_OPEN) {
+ change.taskInfo?.let { taskInfoList.add(it) }
+ transitionTypeList.add(change.mode)
+ }
+ }
+ transitionToTransitionChanges.put(
+ transition,
+ TransitionChanges(taskInfoList, transitionTypeList)
+ )
+ }
+ }
+
+ override fun onTransitionStarting(transition: IBinder) {}
+
+ override fun onTransitionMerged(merged: IBinder, playing: IBinder) {}
+
+ override fun onTransitionFinished(transition: IBinder, aborted: Boolean) {
+ val taskInfoList =
+ transitionToTransitionChanges.getOrDefault(transition, TransitionChanges()).taskInfoList
+ val typeList =
+ transitionToTransitionChanges
+ .getOrDefault(transition, TransitionChanges())
+ .transitionTypeList
+ transitionToTransitionChanges.remove(transition)
+
+ for ((index, taskInfo) in taskInfoList.withIndex()) {
+ if (
+ TransitionUtil.isOpeningType(typeList[index]) &&
+ taskInfo.windowingMode == WINDOWING_MODE_FREEFORM
+ ) {
+ notifyTaskStackTransitionObserverListeners(taskInfo)
+ }
+ }
+ }
+
+ fun addTaskStackTransitionObserverListener(
+ taskStackTransitionObserverListener: TaskStackTransitionObserverListener,
+ executor: Executor
+ ) {
+ taskStackTransitionObserverListeners[taskStackTransitionObserverListener] = executor
+ }
+
+ fun removeTaskStackTransitionObserverListener(
+ taskStackTransitionObserverListener: TaskStackTransitionObserverListener
+ ) {
+ taskStackTransitionObserverListeners.remove(taskStackTransitionObserverListener)
+ }
+
+ private fun notifyTaskStackTransitionObserverListeners(taskInfo: RunningTaskInfo) {
+ taskStackTransitionObserverListeners.forEach { (listener, executor) ->
+ executor.execute { listener.onTaskMovedToFrontThroughTransition(taskInfo) }
+ }
+ }
+
+ /** Listener to use to get updates regarding task stack from this observer */
+ interface TaskStackTransitionObserverListener {
+ /** Called when a task is moved to front. */
+ fun onTaskMovedToFrontThroughTransition(taskInfo: RunningTaskInfo) {}
+ }
+
+ private data class TransitionChanges(
+ val taskInfoList: MutableList<RunningTaskInfo> = ArrayList(),
+ val transitionTypeList: MutableList<Int> = ArrayList()
+ )
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index cc995ea..b6a18e5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -1524,6 +1524,7 @@
prepareExitSplitScreen(mTopStageAfterFoldDismiss, wct);
mSplitTransitions.startDismissTransition(wct, this,
mTopStageAfterFoldDismiss, EXIT_REASON_DEVICE_FOLDED);
+ setSplitsVisible(false);
} else {
exitSplitScreen(
mTopStageAfterFoldDismiss == STAGE_TYPE_MAIN ? mMainStage : mSideStage,
@@ -1893,6 +1894,10 @@
// will be canceled.
options.setPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
options.setPendingIntentBackgroundActivityLaunchAllowedByPermission(true);
+
+ // TODO (b/336477473): Disallow enter PiP when launching a task in split by default;
+ // this might have to be changed as more split-to-pip cujs are defined.
+ options.setDisallowEnterPictureInPictureWhileLaunching(true);
opts.putAll(options.toBundle());
}
@@ -2757,6 +2762,14 @@
// cases above and it is not already visible
return null;
} else {
+ if (triggerTask.parentTaskId == mMainStage.mRootTaskInfo.taskId
+ || triggerTask.parentTaskId == mSideStage.mRootTaskInfo.taskId) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "handleRequest: transition=%d "
+ + "restoring to split", request.getDebugId());
+ out = new WindowContainerTransaction();
+ mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(),
+ TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, false /* resizeAnim */);
+ }
if (isOpening && getStageOfTask(triggerTask) != null) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "handleRequest: transition=%d enter split",
request.getDebugId());
@@ -3103,7 +3116,7 @@
// Includes TRANSIT_CHANGE to cover reparenting top-most task to split.
mainChild = change;
} else if (sideChild == null && stageType == STAGE_TYPE_SIDE
- && isOpeningType(change.getMode())) {
+ && (isOpeningType(change.getMode()) || change.getMode() == TRANSIT_CHANGE)) {
sideChild = change;
} else if (stageType != STAGE_TYPE_UNDEFINED && change.getMode() == TRANSIT_TO_BACK) {
// Collect all to back task's and evict them when transition finished.
@@ -3114,7 +3127,8 @@
SplitScreenTransitions.EnterSession pendingEnter = mSplitTransitions.mPendingEnter;
if (pendingEnter.mExtraTransitType
== TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE) {
- // Open to side should only be used when split already active and foregorund.
+ // Open to side should only be used when split already active and foregorund or when
+ // app is restoring to split from fullscreen.
if (mainChild == null && sideChild == null) {
Log.w(TAG, splitFailureMessage("startPendingEnterAnimation",
"Launched a task in split, but didn't receive any task in transition."));
@@ -3201,6 +3215,22 @@
mPausingTasks.clear();
});
+ if (info.getType() == TRANSIT_CHANGE && !isSplitActive()
+ && pendingEnter.mExtraTransitType == TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE) {
+ if (finalMainChild != null && finalSideChild == null) {
+ requestEnterSplitSelect(finalMainChild.getTaskInfo(),
+ new WindowContainerTransaction(),
+ getMainStagePosition(), finalMainChild.getStartAbsBounds());
+ } else if (finalSideChild != null && finalMainChild == null) {
+ requestEnterSplitSelect(finalSideChild.getTaskInfo(),
+ new WindowContainerTransaction(),
+ getSideStagePosition(), finalSideChild.getStartAbsBounds());
+ } else {
+ throw new IllegalStateException(
+ "Attempting to restore to split but reparenting change not found");
+ }
+ }
+
finishEnterSplitScreen(finishT);
addDividerBarToTransition(info, true /* show */);
return true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index a4ade1b..4d597ca 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
@@ -99,10 +99,12 @@
private DragPositioningCallback mDragPositioningCallback;
private DragResizeInputListener mDragResizeListener;
private DragDetector mDragDetector;
-
+ private Runnable mCurrentViewHostRunnable = null;
private RelayoutParams mRelayoutParams = new RelayoutParams();
private final WindowDecoration.RelayoutResult<WindowDecorLinearLayout> mResult =
new WindowDecoration.RelayoutResult<>();
+ private final Runnable mViewHostRunnable =
+ () -> updateViewHost(mRelayoutParams, null /* onDrawTransaction */, mResult);
private final Point mPositionInParent = new Point();
private HandleMenu mHandleMenu;
@@ -194,17 +196,88 @@
// position and crop are set.
final boolean shouldSetTaskPositionAndCrop = !DesktopModeStatus.isVeiledResizeEnabled()
&& mTaskDragResizer.isResizingOrAnimating();
- // Use |applyStartTransactionOnDraw| so that the transaction (that applies task crop) is
- // synced with the buffer transaction (that draws the View). Both will be shown on screen
- // at the same, whereas applying them independently causes flickering. See b/270202228.
- relayout(taskInfo, t, t, true /* applyStartTransactionOnDraw */,
- shouldSetTaskPositionAndCrop);
+ // For headers only (i.e. in freeform): use |applyStartTransactionOnDraw| so that the
+ // transaction (that applies task crop) is synced with the buffer transaction (that draws
+ // the View). Both will be shown on screen at the same, whereas applying them independently
+ // causes flickering. See b/270202228.
+ final boolean applyTransactionOnDraw =
+ taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM;
+ relayout(taskInfo, t, t, applyTransactionOnDraw, shouldSetTaskPositionAndCrop);
+ if (!applyTransactionOnDraw) {
+ t.apply();
+ }
}
void relayout(ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) {
Trace.beginSection("DesktopModeWindowDecoration#relayout");
+ if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
+ // The Task is in Freeform mode -> show its header in sync since it's an integral part
+ // of the window itself - a delayed header might cause bad UX.
+ relayoutInSync(taskInfo, startT, finishT, applyStartTransactionOnDraw,
+ shouldSetTaskPositionAndCrop);
+ } else {
+ // The Task is outside Freeform mode -> allow the handle view to be delayed since the
+ // handle is just a small addition to the window.
+ relayoutWithDelayedViewHost(taskInfo, startT, finishT, applyStartTransactionOnDraw,
+ shouldSetTaskPositionAndCrop);
+ }
+ Trace.endSection();
+ }
+
+ /** Run the whole relayout phase immediately without delay. */
+ private void relayoutInSync(ActivityManager.RunningTaskInfo taskInfo,
+ SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
+ boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) {
+ // Clear the current ViewHost runnable as we will update the ViewHost here
+ clearCurrentViewHostRunnable();
+ updateRelayoutParamsAndSurfaces(taskInfo, startT, finishT, applyStartTransactionOnDraw,
+ shouldSetTaskPositionAndCrop);
+ if (mResult.mRootView != null) {
+ updateViewHost(mRelayoutParams, startT, mResult);
+ }
+ }
+
+ /**
+ * Clear the current ViewHost runnable - to ensure it doesn't run once relayout params have been
+ * updated.
+ */
+ private void clearCurrentViewHostRunnable() {
+ if (mCurrentViewHostRunnable != null) {
+ mHandler.removeCallbacks(mCurrentViewHostRunnable);
+ mCurrentViewHostRunnable = null;
+ }
+ }
+
+ /**
+ * Relayout the window decoration but repost some of the work, to unblock the current callstack.
+ */
+ private void relayoutWithDelayedViewHost(ActivityManager.RunningTaskInfo taskInfo,
+ SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
+ boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) {
+ if (applyStartTransactionOnDraw) {
+ throw new IllegalArgumentException(
+ "We cannot both sync viewhost ondraw and delay viewhost creation.");
+ }
+ // Clear the current ViewHost runnable as we will update the ViewHost here
+ clearCurrentViewHostRunnable();
+ updateRelayoutParamsAndSurfaces(taskInfo, startT, finishT,
+ false /* applyStartTransactionOnDraw */, shouldSetTaskPositionAndCrop);
+ if (mResult.mRootView == null) {
+ // This means something blocks the window decor from showing, e.g. the task is hidden.
+ // Nothing is set up in this case including the decoration surface.
+ return;
+ }
+ // Store the current runnable so it can be removed if we start a new relayout.
+ mCurrentViewHostRunnable = mViewHostRunnable;
+ mHandler.post(mCurrentViewHostRunnable);
+ }
+
+ private void updateRelayoutParamsAndSurfaces(ActivityManager.RunningTaskInfo taskInfo,
+ SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
+ boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) {
+ Trace.beginSection("DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces");
if (isHandleMenuActive()) {
mHandleMenu.relayout(startT);
}
@@ -216,8 +289,8 @@
final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
final WindowContainerTransaction wct = new WindowContainerTransaction();
- Trace.beginSection("DesktopModeWindowDecoration#relayout-inner");
- relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
+ Trace.beginSection("DesktopModeWindowDecoration#relayout-updateViewsAndSurfaces");
+ updateViewsAndSurfaces(mRelayoutParams, startT, finishT, wct, oldRootView, mResult);
Trace.endSection();
// After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
@@ -228,37 +301,12 @@
if (mResult.mRootView == null) {
// This means something blocks the window decor from showing, e.g. the task is hidden.
// Nothing is set up in this case including the decoration surface.
- Trace.endSection(); // DesktopModeWindowDecoration#relayout
+ Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces
return;
}
if (oldRootView != mResult.mRootView) {
- if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_app_handle) {
- mWindowDecorViewHolder = new AppHandleViewHolder(
- mResult.mRootView,
- mOnCaptionTouchListener,
- mOnCaptionButtonClickListener
- );
- } else if (mRelayoutParams.mLayoutResId
- == R.layout.desktop_mode_app_header) {
- loadAppInfoIfNeeded();
- mWindowDecorViewHolder = new AppHeaderViewHolder(
- mResult.mRootView,
- mOnCaptionTouchListener,
- mOnCaptionButtonClickListener,
- mOnCaptionLongClickListener,
- mOnCaptionGenericMotionListener,
- mAppName,
- mAppIconBitmap,
- () -> {
- if (!isMaximizeMenuActive()) {
- createMaximizeMenu();
- }
- return Unit.INSTANCE;
- });
- } else {
- throw new IllegalArgumentException("Unexpected layout resource id");
- }
+ mWindowDecorViewHolder = createViewHolder();
}
Trace.beginSection("DesktopModeWindowDecoration#relayout-binding");
mWindowDecorViewHolder.bindData(mTaskInfo);
@@ -269,16 +317,18 @@
closeMaximizeMenu();
}
- final boolean isFreeform =
- taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM;
- final boolean isDragResizeable = isFreeform && taskInfo.isResizeable;
- if (!isDragResizeable) {
+ updateDragResizeListener(oldDecorationSurface);
+ updateMaximizeMenu(startT);
+ Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces
+ }
+
+ private void updateDragResizeListener(SurfaceControl oldDecorationSurface) {
+ if (!isDragResizable(mTaskInfo)) {
if (!mTaskInfo.positionInParent.equals(mPositionInParent)) {
// We still want to track caption bar's exclusion region on a non-resizeable task.
updateExclusionRegion();
}
closeDragResizeListener();
- Trace.endSection(); // DesktopModeWindowDecoration#relayout
return;
}
@@ -312,15 +362,51 @@
|| !mTaskInfo.positionInParent.equals(mPositionInParent)) {
updateExclusionRegion();
}
+ }
- if (isMaximizeMenuActive()) {
- if (!mTaskInfo.isVisible()) {
- closeMaximizeMenu();
- } else {
- mMaximizeMenu.positionMenu(calculateMaximizeMenuPosition(), startT);
- }
+ private static boolean isDragResizable(ActivityManager.RunningTaskInfo taskInfo) {
+ final boolean isFreeform =
+ taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM;
+ return isFreeform && taskInfo.isResizeable;
+ }
+
+ private void updateMaximizeMenu(SurfaceControl.Transaction startT) {
+ if (!isDragResizable(mTaskInfo) || !isMaximizeMenuActive()) {
+ return;
}
- Trace.endSection(); // DesktopModeWindowDecoration#relayout
+ if (!mTaskInfo.isVisible()) {
+ closeMaximizeMenu();
+ } else {
+ mMaximizeMenu.positionMenu(calculateMaximizeMenuPosition(), startT);
+ }
+ }
+
+ private WindowDecorationViewHolder createViewHolder() {
+ if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_app_handle) {
+ return new AppHandleViewHolder(
+ mResult.mRootView,
+ mOnCaptionTouchListener,
+ mOnCaptionButtonClickListener
+ );
+ } else if (mRelayoutParams.mLayoutResId
+ == R.layout.desktop_mode_app_header) {
+ loadAppInfoIfNeeded();
+ return new AppHeaderViewHolder(
+ mResult.mRootView,
+ mOnCaptionTouchListener,
+ mOnCaptionButtonClickListener,
+ mOnCaptionLongClickListener,
+ mOnCaptionGenericMotionListener,
+ mAppName,
+ mAppIconBitmap,
+ () -> {
+ if (!isMaximizeMenuActive()) {
+ createMaximizeMenu();
+ }
+ return Unit.INSTANCE;
+ });
+ }
+ throw new IllegalArgumentException("Unexpected layout resource id");
}
@VisibleForTesting
@@ -818,12 +904,12 @@
// We want handle to remain pressed if the pointer moves outside of it during a drag.
handle.setPressed((inHandle && action == ACTION_DOWN)
|| (handle.isPressed() && action != ACTION_UP && action != ACTION_CANCEL));
- if (isHandleMenuActive() && !isMenuAboveStatusBar()) {
+ if (isHandleMenuActive() && !isHandleMenuAboveStatusBar()) {
mHandleMenu.checkMotionEvent(ev);
}
}
- private boolean isMenuAboveStatusBar() {
+ private boolean isHandleMenuAboveStatusBar() {
return Flags.enableAdditionalWindowsAboveStatusBar() && !mTaskInfo.isFreeform();
}
@@ -838,6 +924,7 @@
closeHandleMenu();
mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId);
disposeResizeVeil();
+ clearCurrentViewHostRunnable();
super.close();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
index 82c399a..fe1c9c3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtility.java
@@ -22,12 +22,16 @@
import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP;
import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_UNDEFINED;
+import android.content.Context;
import android.graphics.PointF;
import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.view.SurfaceControl;
+import com.android.window.flags.Flags;
+import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.shared.DesktopModeStatus;
/**
* Utility class that contains logic common to classes implementing {@link DragPositioningCallback}
@@ -35,11 +39,11 @@
* and applying that change to the task bounds when applicable.
*/
public class DragPositioningCallbackUtility {
-
/**
* Determine the delta between input's current point and the input start point.
- * @param inputX current input x coordinate
- * @param inputY current input y coordinate
+ *
+ * @param inputX current input x coordinate
+ * @param inputY current input y coordinate
* @param repositionStartPoint initial input coordinate
* @return delta between these two points
*/
@@ -52,13 +56,14 @@
/**
* Based on type of resize and delta provided, calculate the new bounds to display for this
* task.
- * @param ctrlType type of drag being performed
- * @param repositionTaskBounds the bounds the task is being repositioned to
+ *
+ * @param ctrlType type of drag being performed
+ * @param repositionTaskBounds the bounds the task is being repositioned to
* @param taskBoundsAtDragStart the bounds of the task on the first drag input event
- * @param stableBounds bounds that represent the resize limit of this task
- * @param delta difference between start input and current input in x/y coordinates
- * @param displayController task's display controller
- * @param windowDecoration window decoration of the task being dragged
+ * @param stableBounds bounds that represent the resize limit of this task
+ * @param delta difference between start input and current input in x/y
+ * coordinates
+ * @param windowDecoration window decoration of the task being dragged
* @return whether this method changed repositionTaskBounds
*/
static boolean changeBounds(int ctrlType, Rect repositionTaskBounds, Rect taskBoundsAtDragStart,
@@ -142,8 +147,9 @@
/**
* If task bounds are outside of provided drag area, snap the bounds to be just inside the
* drag area.
+ *
* @param repositionTaskBounds bounds determined by task positioner
- * @param validDragArea the area that task must be positioned inside
+ * @param validDragArea the area that task must be positioned inside
* @return whether bounds were modified
*/
public static boolean snapTaskBoundsIfNecessary(Rect repositionTaskBounds, Rect validDragArea) {
@@ -170,18 +176,38 @@
private static float getMinWidth(DisplayController displayController,
WindowDecoration windowDecoration) {
- return windowDecoration.mTaskInfo.minWidth < 0 ? getDefaultMinSize(displayController,
+ return windowDecoration.mTaskInfo.minWidth < 0 ? getDefaultMinWidth(displayController,
windowDecoration)
: windowDecoration.mTaskInfo.minWidth;
}
private static float getMinHeight(DisplayController displayController,
WindowDecoration windowDecoration) {
- return windowDecoration.mTaskInfo.minHeight < 0 ? getDefaultMinSize(displayController,
+ return windowDecoration.mTaskInfo.minHeight < 0 ? getDefaultMinHeight(displayController,
windowDecoration)
: windowDecoration.mTaskInfo.minHeight;
}
+ private static float getDefaultMinWidth(DisplayController displayController,
+ WindowDecoration windowDecoration) {
+ if (isSizeConstraintForDesktopModeEnabled(windowDecoration.mDecorWindowContext)) {
+ return WindowDecoration.loadDimensionPixelSize(
+ windowDecoration.mDecorWindowContext.getResources(),
+ R.dimen.desktop_mode_minimum_window_width);
+ }
+ return getDefaultMinSize(displayController, windowDecoration);
+ }
+
+ private static float getDefaultMinHeight(DisplayController displayController,
+ WindowDecoration windowDecoration) {
+ if (isSizeConstraintForDesktopModeEnabled(windowDecoration.mDecorWindowContext)) {
+ return WindowDecoration.loadDimensionPixelSize(
+ windowDecoration.mDecorWindowContext.getResources(),
+ R.dimen.desktop_mode_minimum_window_height);
+ }
+ return getDefaultMinSize(displayController, windowDecoration);
+ }
+
private static float getDefaultMinSize(DisplayController displayController,
WindowDecoration windowDecoration) {
float density = displayController.getDisplayLayout(windowDecoration.mTaskInfo.displayId)
@@ -189,9 +215,15 @@
return windowDecoration.mTaskInfo.defaultMinSize * density;
}
+ private static boolean isSizeConstraintForDesktopModeEnabled(Context context) {
+ return DesktopModeStatus.canEnterDesktopMode(context)
+ && Flags.enableDesktopWindowingSizeConstraints();
+ }
+
interface DragStartListener {
/**
* Inform the implementing class that a drag resize has started
+ *
* @param taskId id of this positioner's {@link WindowDecoration}
*/
void onDragStart(int taskId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
index bfc4e0d..df0836c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
@@ -24,6 +24,7 @@
import static android.view.MotionEvent.ACTION_UP;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -35,6 +36,7 @@
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Color;
+import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.view.MotionEvent;
@@ -70,8 +72,14 @@
private final DesktopModeWindowDecoration mParentDecor;
@VisibleForTesting
AdditionalViewContainer mHandleMenuViewContainer;
+ // Position of the handle menu used for laying out the handle view.
@VisibleForTesting
final PointF mHandleMenuPosition = new PointF();
+ // With the introduction of {@link AdditionalSystemViewContainer}, {@link mHandleMenuPosition}
+ // may be in a different coordinate space than the input coordinates. Therefore, we still care
+ // about the menu's coordinates relative to the display as a whole, so we need to maintain
+ // those as well.
+ final Point mGlobalMenuPosition = new Point();
private final boolean mShouldShowWindowingPill;
private final Bitmap mAppIconBitmap;
private final CharSequence mAppName;
@@ -244,39 +252,23 @@
private void updateHandleMenuPillPositions() {
int menuX;
final int menuY;
+ final Rect taskBounds = mTaskInfo.getConfiguration().windowConfiguration.getBounds();
+ updateGlobalMenuPosition(taskBounds);
if (mLayoutResId == R.layout.desktop_mode_app_header) {
// Align the handle menu to the left side of the caption.
menuX = mMarginMenuStart;
menuY = mMarginMenuTop;
} else {
- final int handleWidth = loadDimensionPixelSize(mContext.getResources(),
- R.dimen.desktop_mode_fullscreen_decor_caption_width);
- final int handleOffset = (mMenuWidth / 2) - (handleWidth / 2);
- final int captionX = mParentDecor.getCaptionX();
- // TODO(b/343561161): This needs to be calculated differently if the task is in
- // top/bottom split.
if (Flags.enableAdditionalWindowsAboveStatusBar()) {
- final Rect leftOrTopStageBounds = new Rect();
- if (mSplitScreenController.getSplitPosition(mTaskInfo.taskId)
- == SPLIT_POSITION_BOTTOM_OR_RIGHT) {
- mSplitScreenController.getStageBounds(leftOrTopStageBounds, new Rect());
- }
// In a focused decor, we use global coordinates for handle menu. Therefore we
// need to account for other factors like split stage and menu/handle width to
// center the menu.
final DisplayLayout layout = mDisplayController
.getDisplayLayout(mTaskInfo.displayId);
- menuX = captionX + handleOffset - (layout.width() / 2);
- if (mSplitScreenController.getSplitPosition(mTaskInfo.taskId)
- == SPLIT_POSITION_BOTTOM_OR_RIGHT && layout.isLandscape()) {
- // If this task in the right stage, we need to offset by left stage's width
- menuX += leftOrTopStageBounds.width();
- }
- menuY = mMarginMenuStart - ((layout.height() - mMenuHeight) / 2);
+ menuX = mGlobalMenuPosition.x + ((mMenuWidth - layout.width()) / 2);
+ menuY = mGlobalMenuPosition.y + ((mMenuHeight - layout.height()) / 2);
} else {
- final int captionWidth = mTaskInfo.getConfiguration()
- .windowConfiguration.getBounds().width();
- menuX = (captionWidth / 2) - (mMenuWidth / 2);
+ menuX = (taskBounds.width() / 2) - (mMenuWidth / 2);
menuY = mMarginMenuTop;
}
}
@@ -284,6 +276,36 @@
mHandleMenuPosition.set(menuX, menuY);
}
+ private void updateGlobalMenuPosition(Rect taskBounds) {
+ if (mTaskInfo.isFreeform()) {
+ mGlobalMenuPosition.set(taskBounds.left + mMarginMenuStart,
+ taskBounds.top + mMarginMenuTop);
+ } else if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
+ mGlobalMenuPosition.set(
+ (taskBounds.width() / 2) - (mMenuWidth / 2) + mMarginMenuStart,
+ mMarginMenuTop
+ );
+ } else if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) {
+ final int splitPosition = mSplitScreenController.getSplitPosition(mTaskInfo.taskId);
+ final Rect leftOrTopStageBounds = new Rect();
+ final Rect rightOrBottomStageBounds = new Rect();
+ mSplitScreenController.getStageBounds(leftOrTopStageBounds,
+ rightOrBottomStageBounds);
+ // TODO(b/343561161): This needs to be calculated differently if the task is in
+ // top/bottom split.
+ if (splitPosition == SPLIT_POSITION_BOTTOM_OR_RIGHT) {
+ mGlobalMenuPosition.set(leftOrTopStageBounds.width()
+ + (rightOrBottomStageBounds.width() / 2)
+ - (mMenuWidth / 2) + mMarginMenuStart,
+ mMarginMenuTop);
+ } else if (splitPosition == SPLIT_POSITION_TOP_OR_LEFT) {
+ mGlobalMenuPosition.set((leftOrTopStageBounds.width() / 2)
+ - (mMenuWidth / 2) + mMarginMenuStart,
+ mMarginMenuTop);
+ }
+ }
+ }
+
/**
* Update pill layout, in case task changes have caused positioning to change.
*/
@@ -302,6 +324,8 @@
* @param ev the MotionEvent to compare against.
*/
void checkMotionEvent(MotionEvent ev) {
+ // If the menu view is above status bar, we can let the views handle input directly.
+ if (isViewAboveStatusBar()) return;
final View handleMenu = mHandleMenuViewContainer.getView();
final HandleMenuImageButton collapse = handleMenu.findViewById(R.id.collapse_menu_button);
final PointF inputPoint = translateInputToLocalSpace(ev);
@@ -314,6 +338,11 @@
}
}
+ private boolean isViewAboveStatusBar() {
+ return Flags.enableAdditionalWindowsAboveStatusBar()
+ && !mTaskInfo.isFreeform();
+ }
+
// Translate the input point from display coordinates to the same space as the handle menu.
private PointF translateInputToLocalSpace(MotionEvent ev) {
return new PointF(ev.getX() - mHandleMenuPosition.x,
@@ -329,10 +358,33 @@
*/
boolean isValidMenuInput(PointF inputPoint) {
if (!viewsLaidOut()) return true;
- return pointInView(
- mHandleMenuViewContainer.getView(),
- inputPoint.x - mHandleMenuPosition.x,
- inputPoint.y - mHandleMenuPosition.y);
+ if (!isViewAboveStatusBar()) {
+ return pointInView(
+ mHandleMenuViewContainer.getView(),
+ inputPoint.x - mHandleMenuPosition.x,
+ inputPoint.y - mHandleMenuPosition.y);
+ } else {
+ // Handle menu exists in a different coordinate space when added to WindowManager.
+ // Therefore we must compare the provided input coordinates to global menu coordinates.
+ // This includes factoring for split stage as input coordinates are relative to split
+ // stage position, not relative to the display as a whole.
+ PointF inputRelativeToMenu = new PointF(
+ inputPoint.x - mGlobalMenuPosition.x,
+ inputPoint.y - mGlobalMenuPosition.y
+ );
+ if (mSplitScreenController.getSplitPosition(mTaskInfo.taskId)
+ == SPLIT_POSITION_BOTTOM_OR_RIGHT) {
+ // TODO(b/343561161): This also needs to be calculated differently if
+ // the task is in top/bottom split.
+ Rect leftStageBounds = new Rect();
+ mSplitScreenController.getStageBounds(leftStageBounds, new Rect());
+ inputRelativeToMenu.x += leftStageBounds.width();
+ }
+ return pointInView(
+ mHandleMenuViewContainer.getView(),
+ inputRelativeToMenu.x,
+ inputRelativeToMenu.y);
+ }
}
private boolean pointInView(View v, float x, float y) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index a08f97c..216990c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -199,8 +199,16 @@
void relayout(RelayoutParams params, SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT, WindowContainerTransaction wct, T rootView,
RelayoutResult<T> outResult) {
- outResult.reset();
+ updateViewsAndSurfaces(params, startT, finishT, wct, rootView, outResult);
+ if (outResult.mRootView != null) {
+ updateViewHost(params, startT, outResult);
+ }
+ }
+ protected void updateViewsAndSurfaces(RelayoutParams params,
+ SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
+ WindowContainerTransaction wct, T rootView, RelayoutResult<T> outResult) {
+ outResult.reset();
if (params.mRunningTaskInfo != null) {
mTaskInfo = params.mRunningTaskInfo;
}
@@ -213,13 +221,38 @@
return;
}
+ inflateIfNeeded(params, wct, rootView, oldLayoutResId, outResult);
+ if (outResult.mRootView == null) {
+ // Didn't manage to create a root view, early out.
+ return;
+ }
+ rootView = null; // Clear it just in case we use it accidentally
+
+ updateCaptionVisibility(outResult.mRootView, mTaskInfo.displayId);
+
+ final Rect taskBounds = mTaskInfo.getConfiguration().windowConfiguration.getBounds();
+ outResult.mWidth = taskBounds.width();
+ outResult.mHeight = taskBounds.height();
+ outResult.mRootView.setTaskFocusState(mTaskInfo.isFocused);
+ final Resources resources = mDecorWindowContext.getResources();
+ outResult.mCaptionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId);
+ outResult.mCaptionWidth = params.mCaptionWidthId != Resources.ID_NULL
+ ? loadDimensionPixelSize(resources, params.mCaptionWidthId) : taskBounds.width();
+ outResult.mCaptionX = (outResult.mWidth - outResult.mCaptionWidth) / 2;
+
+ updateDecorationContainerSurface(startT, outResult);
+ updateCaptionContainerSurface(startT, outResult);
+ updateCaptionInsets(params, wct, outResult, taskBounds);
+ updateTaskSurface(params, startT, finishT, outResult);
+ }
+
+ private void inflateIfNeeded(RelayoutParams params, WindowContainerTransaction wct,
+ T rootView, int oldLayoutResId, RelayoutResult<T> outResult) {
if (rootView == null && params.mLayoutResId == 0) {
throw new IllegalArgumentException("layoutResId and rootView can't both be invalid.");
}
outResult.mRootView = rootView;
- rootView = null; // Clear it just in case we use it accidentally
-
final int oldDensityDpi = mWindowDecorConfig != null
? mWindowDecorConfig.densityDpi : DENSITY_DPI_UNDEFINED;
final int oldNightMode = mWindowDecorConfig != null
@@ -253,25 +286,17 @@
outResult.mRootView = (T) LayoutInflater.from(mDecorWindowContext)
.inflate(params.mLayoutResId, null);
}
+ }
- updateCaptionVisibility(outResult.mRootView, mTaskInfo.displayId);
-
- final Resources resources = mDecorWindowContext.getResources();
- final Configuration taskConfig = mTaskInfo.getConfiguration();
- final Rect taskBounds = taskConfig.windowConfiguration.getBounds();
- final boolean isFullscreen = taskConfig.windowConfiguration.getWindowingMode()
- == WINDOWING_MODE_FULLSCREEN;
- outResult.mWidth = taskBounds.width();
- outResult.mHeight = taskBounds.height();
-
- // DecorationContainerSurface
+ private void updateDecorationContainerSurface(
+ SurfaceControl.Transaction startT, RelayoutResult<T> outResult) {
if (mDecorationContainerSurface == null) {
final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get();
mDecorationContainerSurface = builder
.setName("Decor container of Task=" + mTaskInfo.taskId)
.setContainerLayer()
.setParent(mTaskSurface)
- .setCallsite("WindowDecoration.relayout_1")
+ .setCallsite("WindowDecoration.updateDecorationContainerSurface")
.build();
startT.setTrustedOverlay(mDecorationContainerSurface, true)
@@ -281,101 +306,101 @@
startT.setWindowCrop(mDecorationContainerSurface, outResult.mWidth, outResult.mHeight)
.show(mDecorationContainerSurface);
+ }
- // CaptionContainerSurface, CaptionWindowManager
+ private void updateCaptionContainerSurface(
+ SurfaceControl.Transaction startT, RelayoutResult<T> outResult) {
if (mCaptionContainerSurface == null) {
final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get();
mCaptionContainerSurface = builder
.setName("Caption container of Task=" + mTaskInfo.taskId)
.setContainerLayer()
.setParent(mDecorationContainerSurface)
- .setCallsite("WindowDecoration.relayout_2")
+ .setCallsite("WindowDecoration.updateCaptionContainerSurface")
.build();
}
- outResult.mCaptionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId);
- outResult.mCaptionWidth = params.mCaptionWidthId != Resources.ID_NULL
- ? loadDimensionPixelSize(resources, params.mCaptionWidthId) : taskBounds.width();
- outResult.mCaptionX = (outResult.mWidth - outResult.mCaptionWidth) / 2;
-
startT.setWindowCrop(mCaptionContainerSurface, outResult.mCaptionWidth,
outResult.mCaptionHeight)
.setPosition(mCaptionContainerSurface, outResult.mCaptionX, 0 /* y */)
.setLayer(mCaptionContainerSurface, CAPTION_LAYER_Z_ORDER)
.show(mCaptionContainerSurface);
+ }
- outResult.mRootView.setTaskFocusState(mTaskInfo.isFocused);
-
- // Caption insets
- if (mIsCaptionVisible) {
- // Caption inset is the full width of the task with the |captionHeight| and
- // positioned at the top of the task bounds, also in absolute coordinates.
- // So just reuse the task bounds and adjust the bottom coordinate.
- final Rect captionInsetsRect = new Rect(taskBounds);
- captionInsetsRect.bottom = captionInsetsRect.top + outResult.mCaptionHeight;
-
- // Caption bounding rectangles: these are optional, and are used to present finer
- // insets than traditional |Insets| to apps about where their content is occluded.
- // These are also in absolute coordinates.
- final Rect[] boundingRects;
- final int numOfElements = params.mOccludingCaptionElements.size();
- if (numOfElements == 0) {
- boundingRects = null;
- } else {
- // The customizable region can at most be equal to the caption bar.
- if (params.hasInputFeatureSpy()) {
- outResult.mCustomizableCaptionRegion.set(captionInsetsRect);
- }
- boundingRects = new Rect[numOfElements];
- for (int i = 0; i < numOfElements; i++) {
- final OccludingCaptionElement element =
- params.mOccludingCaptionElements.get(i);
- final int elementWidthPx =
- resources.getDimensionPixelSize(element.mWidthResId);
- boundingRects[i] =
- calculateBoundingRect(element, elementWidthPx, captionInsetsRect);
- // Subtract the regions used by the caption elements, the rest is
- // customizable.
- if (params.hasInputFeatureSpy()) {
- outResult.mCustomizableCaptionRegion.op(boundingRects[i],
- Region.Op.DIFFERENCE);
- }
- }
- }
-
- final WindowDecorationInsets newInsets = new WindowDecorationInsets(
- mTaskInfo.token, mOwner, captionInsetsRect, boundingRects);
- if (!newInsets.equals(mWindowDecorationInsets)) {
- // Add or update this caption as an insets source.
- mWindowDecorationInsets = newInsets;
- mWindowDecorationInsets.addOrUpdate(wct);
- }
- } else {
+ private void updateCaptionInsets(RelayoutParams params, WindowContainerTransaction wct,
+ RelayoutResult<T> outResult, Rect taskBounds) {
+ if (!mIsCaptionVisible) {
if (mWindowDecorationInsets != null) {
mWindowDecorationInsets.remove(wct);
mWindowDecorationInsets = null;
}
+ return;
}
+ // Caption inset is the full width of the task with the |captionHeight| and
+ // positioned at the top of the task bounds, also in absolute coordinates.
+ // So just reuse the task bounds and adjust the bottom coordinate.
+ final Rect captionInsetsRect = new Rect(taskBounds);
+ captionInsetsRect.bottom = captionInsetsRect.top + outResult.mCaptionHeight;
- // Task surface itself
- float shadowRadius;
- final Point taskPosition = mTaskInfo.positionInParent;
- if (isFullscreen) {
- // Shadow is not needed for fullscreen tasks
- shadowRadius = 0;
+ // Caption bounding rectangles: these are optional, and are used to present finer
+ // insets than traditional |Insets| to apps about where their content is occluded.
+ // These are also in absolute coordinates.
+ final Rect[] boundingRects;
+ final int numOfElements = params.mOccludingCaptionElements.size();
+ if (numOfElements == 0) {
+ boundingRects = null;
} else {
- shadowRadius = loadDimension(resources, params.mShadowRadiusId);
+ // The customizable region can at most be equal to the caption bar.
+ if (params.hasInputFeatureSpy()) {
+ outResult.mCustomizableCaptionRegion.set(captionInsetsRect);
+ }
+ final Resources resources = mDecorWindowContext.getResources();
+ boundingRects = new Rect[numOfElements];
+ for (int i = 0; i < numOfElements; i++) {
+ final OccludingCaptionElement element =
+ params.mOccludingCaptionElements.get(i);
+ final int elementWidthPx =
+ resources.getDimensionPixelSize(element.mWidthResId);
+ boundingRects[i] =
+ calculateBoundingRect(element, elementWidthPx, captionInsetsRect);
+ // Subtract the regions used by the caption elements, the rest is
+ // customizable.
+ if (params.hasInputFeatureSpy()) {
+ outResult.mCustomizableCaptionRegion.op(boundingRects[i],
+ Region.Op.DIFFERENCE);
+ }
+ }
}
+ final WindowDecorationInsets newInsets = new WindowDecorationInsets(
+ mTaskInfo.token, mOwner, captionInsetsRect, boundingRects);
+ if (!newInsets.equals(mWindowDecorationInsets)) {
+ // Add or update this caption as an insets source.
+ mWindowDecorationInsets = newInsets;
+ mWindowDecorationInsets.addOrUpdate(wct);
+ }
+ }
+
+ private void updateTaskSurface(RelayoutParams params, SurfaceControl.Transaction startT,
+ SurfaceControl.Transaction finishT, RelayoutResult<T> outResult) {
if (params.mSetTaskPositionAndCrop) {
+ final Point taskPosition = mTaskInfo.positionInParent;
startT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight);
finishT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight)
.setPosition(mTaskSurface, taskPosition.x, taskPosition.y);
}
- startT.setShadowRadius(mTaskSurface, shadowRadius)
- .show(mTaskSurface);
+ float shadowRadius;
+ if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
+ // Shadow is not needed for fullscreen tasks
+ shadowRadius = 0;
+ } else {
+ shadowRadius =
+ loadDimension(mDecorWindowContext.getResources(), params.mShadowRadiusId);
+ }
+ startT.setShadowRadius(mTaskSurface, shadowRadius).show(mTaskSurface);
finishT.setShadowRadius(mTaskSurface, shadowRadius);
+
if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
if (!DesktopModeStatus.isVeiledResizeEnabled()) {
// When fluid resize is enabled, add a background to freeform tasks
@@ -390,7 +415,19 @@
} else if (!DesktopModeStatus.isVeiledResizeEnabled()) {
startT.unsetColor(mTaskSurface);
}
+ }
+ /**
+ * Updates a {@link SurfaceControlViewHost} to connect the window decoration surfaces with our
+ * View hierarchy.
+ *
+ * @param params parameters to use from the last relayout
+ * @param onDrawTransaction a transaction to apply in sync with #onDraw
+ * @param outResult results to use from the last relayout
+ *
+ */
+ protected void updateViewHost(RelayoutParams params,
+ SurfaceControl.Transaction onDrawTransaction, RelayoutResult<T> outResult) {
Trace.beginSection("CaptionViewHostLayout");
if (mCaptionWindowManager == null) {
// Put caption under a container surface because ViewRootImpl sets the destination frame
@@ -399,9 +436,7 @@
mTaskInfo.getConfiguration(), mCaptionContainerSurface,
null /* hostInputToken */);
}
-
- // Caption view
- mCaptionWindowManager.setConfiguration(taskConfig);
+ mCaptionWindowManager.setConfiguration(mTaskInfo.getConfiguration());
final WindowManager.LayoutParams lp =
new WindowManager.LayoutParams(outResult.mCaptionWidth, outResult.mCaptionHeight,
TYPE_APPLICATION,
@@ -414,14 +449,20 @@
mViewHost = mSurfaceControlViewHostFactory.create(mDecorWindowContext, mDisplay,
mCaptionWindowManager);
if (params.mApplyStartTransactionOnDraw) {
- mViewHost.getRootSurfaceControl().applyTransactionOnDraw(startT);
+ if (onDrawTransaction == null) {
+ throw new IllegalArgumentException("Trying to sync a null Transaction");
+ }
+ mViewHost.getRootSurfaceControl().applyTransactionOnDraw(onDrawTransaction);
}
mViewHost.setView(outResult.mRootView, lp);
Trace.endSection();
} else {
Trace.beginSection("CaptionViewHostLayout-relayout");
if (params.mApplyStartTransactionOnDraw) {
- mViewHost.getRootSurfaceControl().applyTransactionOnDraw(startT);
+ if (onDrawTransaction == null) {
+ throw new IllegalArgumentException("Trying to sync a null Transaction");
+ }
+ mViewHost.getRootSurfaceControl().applyTransactionOnDraw(onDrawTransaction);
}
mViewHost.relayout(lp);
Trace.endSection();
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp b/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp
index f813b0d..0fe7a16b 100644
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp
@@ -65,9 +65,11 @@
android_test {
name: "WMShellFlickerTestsSplitScreenGroup2",
+ defaults: ["WMShellFlickerTestsDefault"],
manifest: "AndroidManifest.xml",
package_name: "com.android.wm.shell.flicker.splitscreen",
instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen",
+ test_config_template: "AndroidTestTemplate.xml",
srcs: [
":WMShellFlickerTestsSplitScreenBase-src",
":WMShellFlickerTestsSplitScreenGroup2-src",
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/MultipleShowImeRequestsInSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/MultipleShowImeRequestsInSplitScreen.kt
new file mode 100644
index 0000000..dad5db9
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/MultipleShowImeRequestsInSplitScreen.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.splitscreen
+
+import android.platform.test.annotations.Presubmit
+import android.tools.Rotation
+import android.tools.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.flicker.legacy.FlickerBuilder
+import android.tools.flicker.legacy.LegacyFlickerTest
+import android.tools.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.traces.component.ComponentNameMatcher
+import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.splitscreen.benchmark.MultipleShowImeRequestsInSplitScreenBenchmark
+import com.android.wm.shell.flicker.utils.ICommonAssertions
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test quick switch between two split pairs.
+ *
+ * To run this test: `atest WMShellFlickerTestsSplitScreenGroup2:MultipleShowImeRequestsInSplitScreen`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class MultipleShowImeRequestsInSplitScreen(override val flicker: LegacyFlickerTest) :
+ MultipleShowImeRequestsInSplitScreenBenchmark(flicker), ICommonAssertions {
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ defaultSetup(this)
+ defaultTeardown(this)
+ thisTransition(this)
+ }
+
+ @Presubmit
+ @Test
+ fun imeLayerAlwaysVisible() =
+ flicker.assertLayers {
+ this.isVisible(ComponentNameMatcher.IME)
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(Rotation.ROTATION_0)
+ )
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
index 9045364..d349988 100644
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
@@ -47,7 +47,7 @@
* To run this test: `atest WMShellFlickerTestsSplitScreen:UnlockKeyguardToSplitScreen`
*/
@RequiresDevice
-@Postsubmit
+@FlakyTest(bugId = 293578017)
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -61,7 +61,6 @@
}
@Test
- @FlakyTest(bugId = 293578017)
override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
super.visibleLayersShownMoreThanOneConsecutiveEntry()
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/MultipleShowImeRequestsInSplitScreenBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/MultipleShowImeRequestsInSplitScreenBenchmark.kt
new file mode 100644
index 0000000..2492531
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/MultipleShowImeRequestsInSplitScreenBenchmark.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.splitscreen.benchmark
+
+import android.tools.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.flicker.legacy.FlickerBuilder
+import android.tools.flicker.legacy.LegacyFlickerTest
+import android.tools.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.wm.shell.flicker.utils.SplitScreenUtils
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+abstract class MultipleShowImeRequestsInSplitScreenBenchmark(
+ override val flicker: LegacyFlickerTest
+) : SplitScreenBase(flicker) {
+ override val primaryApp = ImeAppHelper(instrumentation)
+ override val defaultTeardown: FlickerBuilder.() -> Unit
+ get() = {
+ teardown {
+ primaryApp.closeIME(wmHelper)
+ super.defaultTeardown
+ }
+ }
+
+ protected val thisTransition: FlickerBuilder.() -> Unit
+ get() = {
+ setup {
+ SplitScreenUtils.enterSplit(
+ wmHelper,
+ tapl,
+ device,
+ primaryApp,
+ secondaryApp,
+ flicker.scenario.startRotation
+ )
+ // initially open the IME
+ primaryApp.openIME(wmHelper)
+ }
+ transitions {
+ for (i in 1..OPEN_IME_COUNT) {
+ primaryApp.openIME(wmHelper)
+ }
+ }
+ }
+
+ companion object {
+ const val OPEN_IME_COUNT = 30
+
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt
index 4b10603..51074f6 100644
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt
@@ -25,7 +25,7 @@
abstract class SplitScreenBase(flicker: LegacyFlickerTest) : BaseBenchmarkTest(flicker) {
protected val context: Context = instrumentation.context
- protected val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ protected open val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
protected val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
protected open val defaultSetup: FlickerBuilder.() -> Unit = {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index f6f3aa4..1903586 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -123,6 +123,7 @@
private DefaultCrossActivityBackAnimation mDefaultCrossActivityBackAnimation;
private CrossTaskBackAnimation mCrossTaskBackAnimation;
private ShellBackAnimationRegistry mShellBackAnimationRegistry;
+ private Rect mTouchableRegion;
@Before
public void setUp() throws Exception {
@@ -158,6 +159,8 @@
mShellCommandHandler);
mShellInit.init();
mShellExecutor.flushAll();
+ mTouchableRegion = new Rect(0, 0, 100, 100);
+ mController.mTouchableArea.set(mTouchableRegion);
}
private void createNavigationInfo(int backType,
@@ -169,7 +172,8 @@
.setOnBackNavigationDone(new RemoteCallback((bundle) -> {}))
.setOnBackInvokedCallback(mAppCallback)
.setPrepareRemoteAnimation(enableAnimation)
- .setAnimationCallback(isAnimationCallback);
+ .setAnimationCallback(isAnimationCallback)
+ .setTouchableRegion(mTouchableRegion);
createNavigationInfo(builder);
}
@@ -234,7 +238,8 @@
.setType(type)
.setOnBackInvokedCallback(mAppCallback)
.setPrepareRemoteAnimation(true)
- .setOnBackNavigationDone(new RemoteCallback(result)));
+ .setOnBackNavigationDone(new RemoteCallback(result))
+ .setTouchableRegion(mTouchableRegion));
triggerBackGesture();
simulateRemoteAnimationStart();
mShellExecutor.flushAll();
@@ -512,7 +517,8 @@
.setType(type)
.setOnBackInvokedCallback(mAppCallback)
.setPrepareRemoteAnimation(true)
- .setOnBackNavigationDone(new RemoteCallback(result)));
+ .setOnBackNavigationDone(new RemoteCallback(result))
+ .setTouchableRegion(mTouchableRegion));
triggerBackGesture();
simulateRemoteAnimationStart();
mShellExecutor.flushAll();
@@ -543,7 +549,8 @@
createNavigationInfo(new BackNavigationInfo.Builder()
.setType(type)
.setOnBackInvokedCallback(mAppCallback)
- .setOnBackNavigationDone(new RemoteCallback(result)));
+ .setOnBackNavigationDone(new RemoteCallback(result))
+ .setTouchableRegion(mTouchableRegion));
triggerBackGesture();
mShellExecutor.flushAll();
releaseBackGesture();
@@ -570,7 +577,8 @@
createNavigationInfo(new BackNavigationInfo.Builder()
.setType(type)
.setOnBackInvokedCallback(mAppCallback)
- .setOnBackNavigationDone(new RemoteCallback(result)));
+ .setOnBackNavigationDone(new RemoteCallback(result))
+ .setTouchableRegion(mTouchableRegion));
doMotionEvent(MotionEvent.ACTION_CANCEL, 0);
mShellExecutor.flushAll();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index f55c96c..93e4051 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -1170,9 +1170,9 @@
// Verify the update has the removals.
BubbleData.Update update = mUpdateCaptor.getValue();
assertThat(update.removedBubbles.get(0)).isEqualTo(
- Pair.create(mBubbleA2, Bubbles.DISMISS_USER_REMOVED));
+ Pair.create(mBubbleA2, Bubbles.DISMISS_USER_ACCOUNT_REMOVED));
assertThat(update.removedBubbles.get(1)).isEqualTo(
- Pair.create(mBubbleA1, Bubbles.DISMISS_USER_REMOVED));
+ Pair.create(mBubbleA1, Bubbles.DISMISS_USER_ACCOUNT_REMOVED));
// Verify no A bubbles in active or overflow.
assertBubbleListContains(mBubbleC1, mBubbleB3);
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 7122181..0d3cd10 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
@@ -40,14 +40,14 @@
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason
-import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON
-import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT
-import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG
-import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN
+import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON
+import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT
+import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG
+import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN
import com.android.wm.shell.shared.DesktopModeStatus
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.TransitionInfoBuilder
@@ -78,410 +78,397 @@
@RunWith(AndroidTestingRunner::class)
class DesktopModeLoggerTransitionObserverTest {
- @JvmField
- @Rule
- val extendedMockitoRule =
- ExtendedMockitoRule.Builder(this)
- .mockStatic(DesktopModeEventLogger::class.java)
- .mockStatic(DesktopModeStatus::class.java)
- .build()!!
+ @JvmField
+ @Rule
+ val extendedMockitoRule =
+ ExtendedMockitoRule.Builder(this)
+ .mockStatic(DesktopModeEventLogger::class.java)
+ .mockStatic(DesktopModeStatus::class.java)
+ .build()!!
- @Mock lateinit var testExecutor: ShellExecutor
- @Mock private lateinit var mockShellInit: ShellInit
- @Mock private lateinit var transitions: Transitions
- @Mock private lateinit var context: Context
+ @Mock lateinit var testExecutor: ShellExecutor
+ @Mock private lateinit var mockShellInit: ShellInit
+ @Mock private lateinit var transitions: Transitions
+ @Mock private lateinit var context: Context
- private lateinit var transitionObserver: DesktopModeLoggerTransitionObserver
- private lateinit var shellInit: ShellInit
- private lateinit var desktopModeEventLogger: DesktopModeEventLogger
+ private lateinit var transitionObserver: DesktopModeLoggerTransitionObserver
+ private lateinit var shellInit: ShellInit
+ private lateinit var desktopModeEventLogger: DesktopModeEventLogger
- @Before
- fun setup() {
- doReturn(true).`when` { DesktopModeStatus.canEnterDesktopMode(any()) }
- shellInit = Mockito.spy(ShellInit(testExecutor))
- desktopModeEventLogger = mock(DesktopModeEventLogger::class.java)
+ @Before
+ fun setup() {
+ doReturn(true).`when` { DesktopModeStatus.canEnterDesktopMode(any()) }
+ shellInit = Mockito.spy(ShellInit(testExecutor))
+ desktopModeEventLogger = mock(DesktopModeEventLogger::class.java)
- transitionObserver =
- DesktopModeLoggerTransitionObserver(
- context,
- mockShellInit,
- transitions,
- desktopModeEventLogger
- )
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- val initRunnableCaptor = ArgumentCaptor.forClass(Runnable::class.java)
- verify(mockShellInit)
- .addInitCallback(initRunnableCaptor.capture(), same(transitionObserver))
- initRunnableCaptor.value.run()
- } else {
- transitionObserver.onInit()
- }
+ transitionObserver =
+ DesktopModeLoggerTransitionObserver(
+ context, mockShellInit, transitions, desktopModeEventLogger)
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ val initRunnableCaptor = ArgumentCaptor.forClass(Runnable::class.java)
+ verify(mockShellInit).addInitCallback(initRunnableCaptor.capture(), same(transitionObserver))
+ initRunnableCaptor.value.run()
+ } else {
+ transitionObserver.onInit()
+ }
+ }
+
+ @Test
+ fun testRegistersObserverAtInit() {
+ verify(transitions).registerObserver(same(transitionObserver))
+ }
+
+ @Test
+ fun transitOpen_notFreeformWindow_doesNotLogTaskAddedOrSessionEnter() {
+ val change = createChange(TRANSIT_OPEN, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
+ val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
+
+ callOnTransitionReady(transitionInfo)
+
+ verify(desktopModeEventLogger, never()).logSessionEnter(any(), any())
+ verify(desktopModeEventLogger, never()).logTaskAdded(any(), any())
+ }
+
+ @Test
+ fun transitOpen_logTaskAddedAndEnterReasonAppFreeformIntent() {
+ val change = createChange(TRANSIT_OPEN, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
+
+ callOnTransitionReady(transitionInfo)
+ val sessionId = transitionObserver.getLoggerSessionId()
+
+ assertThat(sessionId).isNotNull()
+ verify(desktopModeEventLogger, times(1))
+ .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_FREEFORM_INTENT))
+ verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
+ verifyZeroInteractions(desktopModeEventLogger)
+ }
+
+ @Test
+ fun transitEndDragToDesktop_logTaskAddedAndEnterReasonAppHandleDrag() {
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ // task change is finalised when drag ends
+ val transitionInfo =
+ TransitionInfoBuilder(Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP, 0)
+ .addChange(change)
+ .build()
+
+ callOnTransitionReady(transitionInfo)
+ val sessionId = transitionObserver.getLoggerSessionId()
+
+ assertThat(sessionId).isNotNull()
+ verify(desktopModeEventLogger, times(1))
+ .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_HANDLE_DRAG))
+ verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
+ verifyZeroInteractions(desktopModeEventLogger)
+ }
+
+ @Test
+ fun transitEnterDesktopByButtonTap_logTaskAddedAndEnterReasonButtonTap() {
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val transitionInfo =
+ TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON, 0)
+ .addChange(change)
+ .build()
+
+ callOnTransitionReady(transitionInfo)
+ val sessionId = transitionObserver.getLoggerSessionId()
+
+ assertThat(sessionId).isNotNull()
+ verify(desktopModeEventLogger, times(1))
+ .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_HANDLE_MENU_BUTTON))
+ verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
+ verifyZeroInteractions(desktopModeEventLogger)
+ }
+
+ @Test
+ fun transitEnterDesktopFromAppFromOverview_logTaskAddedAndEnterReasonUnknown() {
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val transitionInfo =
+ TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW, 0)
+ .addChange(change)
+ .build()
+
+ callOnTransitionReady(transitionInfo)
+ val sessionId = transitionObserver.getLoggerSessionId()
+
+ assertThat(sessionId).isNotNull()
+ verify(desktopModeEventLogger, times(1))
+ .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_FROM_OVERVIEW))
+ verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
+ verifyZeroInteractions(desktopModeEventLogger)
+ }
+
+ @Test
+ fun transitEnterDesktopFromKeyboardShortcut_logTaskAddedAndEnterReasonKeyboardShortcut() {
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val transitionInfo =
+ TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT, 0)
+ .addChange(change)
+ .build()
+
+ callOnTransitionReady(transitionInfo)
+ val sessionId = transitionObserver.getLoggerSessionId()
+
+ assertThat(sessionId).isNotNull()
+ verify(desktopModeEventLogger, times(1))
+ .logSessionEnter(eq(sessionId!!), eq(EnterReason.KEYBOARD_SHORTCUT_ENTER))
+ verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
+ verifyZeroInteractions(desktopModeEventLogger)
+ }
+
+ @Test
+ fun transitEnterDesktopFromUnknown_logTaskAddedAndEnterReasonUnknown() {
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val transitionInfo =
+ TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN, 0).addChange(change).build()
+
+ callOnTransitionReady(transitionInfo)
+ val sessionId = transitionObserver.getLoggerSessionId()
+
+ assertThat(sessionId).isNotNull()
+ verify(desktopModeEventLogger, times(1))
+ .logSessionEnter(eq(sessionId!!), eq(EnterReason.UNKNOWN_ENTER))
+ verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
+ verifyZeroInteractions(desktopModeEventLogger)
+ }
+
+ @Test
+ fun transitWake_logTaskAddedAndEnterReasonScreenOn() {
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val transitionInfo = TransitionInfoBuilder(TRANSIT_WAKE, 0).addChange(change).build()
+
+ callOnTransitionReady(transitionInfo)
+ val sessionId = transitionObserver.getLoggerSessionId()
+
+ assertThat(sessionId).isNotNull()
+ verify(desktopModeEventLogger, times(1))
+ .logSessionEnter(eq(sessionId!!), eq(EnterReason.SCREEN_ON))
+ verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
+ verifyZeroInteractions(desktopModeEventLogger)
+ }
+
+ @Test
+ fun transitSleep_logTaskAddedAndExitReasonScreenOff_sessionIdNull() {
+ val sessionId = 1
+ // add a freeform task
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ transitionObserver.setLoggerSessionId(sessionId)
+
+ val transitionInfo = TransitionInfoBuilder(TRANSIT_SLEEP).build()
+ callOnTransitionReady(transitionInfo)
+
+ verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
+ verify(desktopModeEventLogger, times(1))
+ .logSessionExit(eq(sessionId), eq(ExitReason.SCREEN_OFF))
+ verifyZeroInteractions(desktopModeEventLogger)
+ assertThat(transitionObserver.getLoggerSessionId()).isNull()
+ }
+
+ @Test
+ fun transitExitDesktopTaskDrag_logTaskRemovedAndExitReasonDragToExit_sessionIdNull() {
+ val sessionId = 1
+ // add a freeform task
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ transitionObserver.setLoggerSessionId(sessionId)
+
+ // window mode changing from FREEFORM to FULLSCREEN
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
+ val transitionInfo =
+ TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG).addChange(change).build()
+ callOnTransitionReady(transitionInfo)
+
+ verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
+ verify(desktopModeEventLogger, times(1))
+ .logSessionExit(eq(sessionId), eq(ExitReason.DRAG_TO_EXIT))
+ verifyZeroInteractions(desktopModeEventLogger)
+ assertThat(transitionObserver.getLoggerSessionId()).isNull()
+ }
+
+ @Test
+ fun transitExitDesktopAppHandleButton_logTaskRemovedAndExitReasonButton_sessionIdNull() {
+ val sessionId = 1
+ // add a freeform task
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ transitionObserver.setLoggerSessionId(sessionId)
+
+ // window mode changing from FREEFORM to FULLSCREEN
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
+ val transitionInfo =
+ TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON)
+ .addChange(change)
+ .build()
+ callOnTransitionReady(transitionInfo)
+
+ verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
+ verify(desktopModeEventLogger, times(1))
+ .logSessionExit(eq(sessionId), eq(ExitReason.APP_HANDLE_MENU_BUTTON_EXIT))
+ verifyZeroInteractions(desktopModeEventLogger)
+ assertThat(transitionObserver.getLoggerSessionId()).isNull()
+ }
+
+ @Test
+ fun transitExitDesktopUsingKeyboard_logTaskRemovedAndExitReasonKeyboard_sessionIdNull() {
+ val sessionId = 1
+ // add a freeform task
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ transitionObserver.setLoggerSessionId(sessionId)
+
+ // window mode changing from FREEFORM to FULLSCREEN
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
+ val transitionInfo =
+ TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT).addChange(change).build()
+ callOnTransitionReady(transitionInfo)
+
+ verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
+ verify(desktopModeEventLogger, times(1))
+ .logSessionExit(eq(sessionId), eq(ExitReason.KEYBOARD_SHORTCUT_EXIT))
+ verifyZeroInteractions(desktopModeEventLogger)
+ assertThat(transitionObserver.getLoggerSessionId()).isNull()
+ }
+
+ @Test
+ fun transitExitDesktopUnknown_logTaskRemovedAndExitReasonUnknown_sessionIdNull() {
+ val sessionId = 1
+ // add a freeform task
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ transitionObserver.setLoggerSessionId(sessionId)
+
+ // window mode changing from FREEFORM to FULLSCREEN
+ val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
+ val transitionInfo =
+ TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN).addChange(change).build()
+ callOnTransitionReady(transitionInfo)
+
+ verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
+ verify(desktopModeEventLogger, times(1))
+ .logSessionExit(eq(sessionId), eq(ExitReason.UNKNOWN_EXIT))
+ verifyZeroInteractions(desktopModeEventLogger)
+ assertThat(transitionObserver.getLoggerSessionId()).isNull()
+ }
+
+ @Test
+ fun transitToFrontWithFlagRecents_logTaskRemovedAndExitReasonOverview_sessionIdNull() {
+ val sessionId = 1
+ // add a freeform task
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ transitionObserver.setLoggerSessionId(sessionId)
+
+ // recents transition
+ val change = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val transitionInfo =
+ TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS).addChange(change).build()
+ callOnTransitionReady(transitionInfo)
+
+ verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
+ verify(desktopModeEventLogger, times(1))
+ .logSessionExit(eq(sessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW))
+ verifyZeroInteractions(desktopModeEventLogger)
+ assertThat(transitionObserver.getLoggerSessionId()).isNull()
+ }
+
+ @Test
+ fun transitClose_logTaskRemovedAndExitReasonTaskFinished_sessionIdNull() {
+ val sessionId = 1
+ // add a freeform task
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ transitionObserver.setLoggerSessionId(sessionId)
+
+ // task closing
+ val change = createChange(TRANSIT_CLOSE, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
+ val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE).addChange(change).build()
+ callOnTransitionReady(transitionInfo)
+
+ verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
+ verify(desktopModeEventLogger, times(1))
+ .logSessionExit(eq(sessionId), eq(ExitReason.TASK_FINISHED))
+ verifyZeroInteractions(desktopModeEventLogger)
+ assertThat(transitionObserver.getLoggerSessionId()).isNull()
+ }
+
+ @Test
+ fun sessionExitByRecents_cancelledAnimation_sessionRestored() {
+ val sessionId = 1
+ // add a freeform task to an existing session
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ transitionObserver.setLoggerSessionId(sessionId)
+
+ // recents transition sent freeform window to back
+ val change = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ val transitionInfo1 =
+ TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS).addChange(change).build()
+ callOnTransitionReady(transitionInfo1)
+ verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
+ verify(desktopModeEventLogger, times(1))
+ .logSessionExit(eq(sessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW))
+ assertThat(transitionObserver.getLoggerSessionId()).isNull()
+
+ val transitionInfo2 = TransitionInfoBuilder(TRANSIT_NONE).build()
+ callOnTransitionReady(transitionInfo2)
+
+ verify(desktopModeEventLogger, times(1)).logSessionEnter(any(), any())
+ verify(desktopModeEventLogger, times(1)).logTaskAdded(any(), any())
+ }
+
+ @Test
+ fun sessionAlreadyStarted_newFreeformTaskAdded_logsTaskAdded() {
+ val sessionId = 1
+ // add an existing freeform task
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ transitionObserver.setLoggerSessionId(sessionId)
+
+ // new freeform task added
+ val change = createChange(TRANSIT_OPEN, createTaskInfo(2, WINDOWING_MODE_FREEFORM))
+ val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
+ callOnTransitionReady(transitionInfo)
+
+ verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
+ verify(desktopModeEventLogger, never()).logSessionEnter(any(), any())
+ }
+
+ @Test
+ fun sessionAlreadyStarted_freeformTaskRemoved_logsTaskRemoved() {
+ val sessionId = 1
+ // add two existing freeform tasks
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(2, WINDOWING_MODE_FREEFORM))
+ transitionObserver.setLoggerSessionId(sessionId)
+
+ // new freeform task added
+ val change = createChange(TRANSIT_CLOSE, createTaskInfo(2, WINDOWING_MODE_FREEFORM))
+ val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE, 0).addChange(change).build()
+ callOnTransitionReady(transitionInfo)
+
+ verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
+ verify(desktopModeEventLogger, never()).logSessionExit(any(), any())
+ }
+
+ /** Simulate calling the onTransitionReady() method */
+ private fun callOnTransitionReady(transitionInfo: TransitionInfo) {
+ val transition = mock(IBinder::class.java)
+ val startT = mock(SurfaceControl.Transaction::class.java)
+ val finishT = mock(SurfaceControl.Transaction::class.java)
+
+ transitionObserver.onTransitionReady(transition, transitionInfo, startT, finishT)
+ }
+
+ companion object {
+ fun createTaskInfo(taskId: Int, windowMode: Int): ActivityManager.RunningTaskInfo {
+ val taskInfo = ActivityManager.RunningTaskInfo()
+ taskInfo.taskId = taskId
+ taskInfo.configuration.windowConfiguration.windowingMode = windowMode
+
+ return taskInfo
}
- @Test
- fun testRegistersObserverAtInit() {
- verify(transitions).registerObserver(same(transitionObserver))
+ fun createChange(mode: Int, taskInfo: ActivityManager.RunningTaskInfo): Change {
+ val change =
+ Change(
+ WindowContainerToken(mock(IWindowContainerToken::class.java)),
+ mock(SurfaceControl::class.java))
+ change.mode = mode
+ change.taskInfo = taskInfo
+ return change
}
-
- @Test
- fun transitOpen_notFreeformWindow_doesNotLogTaskAddedOrSessionEnter() {
- val change = createChange(TRANSIT_OPEN, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
- val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
-
- callOnTransitionReady(transitionInfo)
-
- verify(desktopModeEventLogger, never()).logSessionEnter(any(), any())
- verify(desktopModeEventLogger, never()).logTaskAdded(any(), any())
- }
-
- @Test
- fun transitOpen_logTaskAddedAndEnterReasonAppFreeformIntent() {
- val change = createChange(TRANSIT_OPEN, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
- val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
-
- callOnTransitionReady(transitionInfo)
- val sessionId = transitionObserver.getLoggerSessionId()
-
- assertThat(sessionId).isNotNull()
- verify(desktopModeEventLogger, times(1))
- .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_FREEFORM_INTENT))
- verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
- verifyZeroInteractions(desktopModeEventLogger)
- }
-
- @Test
- fun transitEndDragToDesktop_logTaskAddedAndEnterReasonAppHandleDrag() {
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
- // task change is finalised when drag ends
- val transitionInfo =
- TransitionInfoBuilder(Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP, 0)
- .addChange(change)
- .build()
-
- callOnTransitionReady(transitionInfo)
- val sessionId = transitionObserver.getLoggerSessionId()
-
- assertThat(sessionId).isNotNull()
- verify(desktopModeEventLogger, times(1))
- .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_HANDLE_DRAG))
- verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
- verifyZeroInteractions(desktopModeEventLogger)
- }
-
- @Test
- fun transitEnterDesktopByButtonTap_logTaskAddedAndEnterReasonButtonTap() {
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
- val transitionInfo =
- TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON, 0)
- .addChange(change)
- .build()
-
- callOnTransitionReady(transitionInfo)
- val sessionId = transitionObserver.getLoggerSessionId()
-
- assertThat(sessionId).isNotNull()
- verify(desktopModeEventLogger, times(1))
- .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_HANDLE_MENU_BUTTON))
- verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
- verifyZeroInteractions(desktopModeEventLogger)
- }
-
- @Test
- // TODO(b/344822506): Update test when we add enter reason for app from overview
- fun transitEnterDesktopFromAppFromOverview_logTaskAddedAndEnterReasonUnknown() {
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
- val transitionInfo =
- TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW, 0)
- .addChange(change)
- .build()
-
- callOnTransitionReady(transitionInfo)
- val sessionId = transitionObserver.getLoggerSessionId()
-
- assertThat(sessionId).isNotNull()
- verify(desktopModeEventLogger, times(1))
- .logSessionEnter(eq(sessionId!!), eq(EnterReason.UNKNOWN_ENTER))
- verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
- verifyZeroInteractions(desktopModeEventLogger)
- }
-
- @Test
- fun transitEnterDesktopFromKeyboardShortcut_logTaskAddedAndEnterReasonKeyboardShortcut() {
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
- val transitionInfo =
- TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT, 0)
- .addChange(change)
- .build()
-
- callOnTransitionReady(transitionInfo)
- val sessionId = transitionObserver.getLoggerSessionId()
-
- assertThat(sessionId).isNotNull()
- verify(desktopModeEventLogger, times(1))
- .logSessionEnter(eq(sessionId!!), eq(EnterReason.KEYBOARD_SHORTCUT_ENTER))
- verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
- verifyZeroInteractions(desktopModeEventLogger)
- }
-
- @Test
- fun transitEnterDesktopFromUnknown_logTaskAddedAndEnterReasonUnknown() {
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
- val transitionInfo =
- TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN, 0).addChange(change).build()
-
- callOnTransitionReady(transitionInfo)
- val sessionId = transitionObserver.getLoggerSessionId()
-
- assertThat(sessionId).isNotNull()
- verify(desktopModeEventLogger, times(1))
- .logSessionEnter(eq(sessionId!!), eq(EnterReason.UNKNOWN_ENTER))
- verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
- verifyZeroInteractions(desktopModeEventLogger)
- }
-
- @Test
- fun transitWake_logTaskAddedAndEnterReasonScreenOn() {
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
- val transitionInfo = TransitionInfoBuilder(TRANSIT_WAKE, 0).addChange(change).build()
-
- callOnTransitionReady(transitionInfo)
- val sessionId = transitionObserver.getLoggerSessionId()
-
- assertThat(sessionId).isNotNull()
- verify(desktopModeEventLogger, times(1))
- .logSessionEnter(eq(sessionId!!), eq(EnterReason.SCREEN_ON))
- verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
- verifyZeroInteractions(desktopModeEventLogger)
- }
-
- @Test
- fun transitSleep_logTaskAddedAndExitReasonScreenOff_sessionIdNull() {
- val sessionId = 1
- // add a freeform task
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
- transitionObserver.setLoggerSessionId(sessionId)
-
- val transitionInfo = TransitionInfoBuilder(TRANSIT_SLEEP).build()
- callOnTransitionReady(transitionInfo)
-
- verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
- verify(desktopModeEventLogger, times(1))
- .logSessionExit(eq(sessionId), eq(ExitReason.SCREEN_OFF))
- verifyZeroInteractions(desktopModeEventLogger)
- assertThat(transitionObserver.getLoggerSessionId()).isNull()
- }
-
- @Test
- fun transitExitDesktopTaskDrag_logTaskRemovedAndExitReasonDragToExit_sessionIdNull() {
- val sessionId = 1
- // add a freeform task
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
- transitionObserver.setLoggerSessionId(sessionId)
-
- // window mode changing from FREEFORM to FULLSCREEN
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
- val transitionInfo =
- TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG).addChange(change).build()
- callOnTransitionReady(transitionInfo)
-
- verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
- verify(desktopModeEventLogger, times(1))
- .logSessionExit(eq(sessionId), eq(ExitReason.DRAG_TO_EXIT))
- verifyZeroInteractions(desktopModeEventLogger)
- assertThat(transitionObserver.getLoggerSessionId()).isNull()
- }
-
- @Test
- fun transitExitDesktopAppHandleButton_logTaskRemovedAndExitReasonButton_sessionIdNull() {
- val sessionId = 1
- // add a freeform task
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
- transitionObserver.setLoggerSessionId(sessionId)
-
- // window mode changing from FREEFORM to FULLSCREEN
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
- val transitionInfo =
- TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON)
- .addChange(change)
- .build()
- callOnTransitionReady(transitionInfo)
-
- verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
- verify(desktopModeEventLogger, times(1))
- .logSessionExit(eq(sessionId), eq(ExitReason.APP_HANDLE_MENU_BUTTON_EXIT))
- verifyZeroInteractions(desktopModeEventLogger)
- assertThat(transitionObserver.getLoggerSessionId()).isNull()
- }
-
- @Test
- fun transitExitDesktopUsingKeyboard_logTaskRemovedAndExitReasonKeyboard_sessionIdNull() {
- val sessionId = 1
- // add a freeform task
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
- transitionObserver.setLoggerSessionId(sessionId)
-
- // window mode changing from FREEFORM to FULLSCREEN
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
- val transitionInfo =
- TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT)
- .addChange(change)
- .build()
- callOnTransitionReady(transitionInfo)
-
- verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
- verify(desktopModeEventLogger, times(1))
- .logSessionExit(eq(sessionId), eq(ExitReason.KEYBOARD_SHORTCUT_EXIT))
- verifyZeroInteractions(desktopModeEventLogger)
- assertThat(transitionObserver.getLoggerSessionId()).isNull()
- }
-
- @Test
- fun transitExitDesktopUnknown_logTaskRemovedAndExitReasonUnknown_sessionIdNull() {
- val sessionId = 1
- // add a freeform task
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
- transitionObserver.setLoggerSessionId(sessionId)
-
- // window mode changing from FREEFORM to FULLSCREEN
- val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
- val transitionInfo =
- TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN).addChange(change).build()
- callOnTransitionReady(transitionInfo)
-
- verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
- verify(desktopModeEventLogger, times(1))
- .logSessionExit(eq(sessionId), eq(ExitReason.UNKNOWN_EXIT))
- verifyZeroInteractions(desktopModeEventLogger)
- assertThat(transitionObserver.getLoggerSessionId()).isNull()
- }
-
- @Test
- fun transitToFrontWithFlagRecents_logTaskRemovedAndExitReasonOverview_sessionIdNull() {
- val sessionId = 1
- // add a freeform task
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
- transitionObserver.setLoggerSessionId(sessionId)
-
- // recents transition
- val change = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
- val transitionInfo =
- TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
- .addChange(change)
- .build()
- callOnTransitionReady(transitionInfo)
-
- verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
- verify(desktopModeEventLogger, times(1))
- .logSessionExit(eq(sessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW))
- verifyZeroInteractions(desktopModeEventLogger)
- assertThat(transitionObserver.getLoggerSessionId()).isNull()
- }
-
- @Test
- fun transitClose_logTaskRemovedAndExitReasonTaskFinished_sessionIdNull() {
- val sessionId = 1
- // add a freeform task
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
- transitionObserver.setLoggerSessionId(sessionId)
-
- // task closing
- val change = createChange(TRANSIT_CLOSE, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
- val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE).addChange(change).build()
- callOnTransitionReady(transitionInfo)
-
- verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
- verify(desktopModeEventLogger, times(1))
- .logSessionExit(eq(sessionId), eq(ExitReason.TASK_FINISHED))
- verifyZeroInteractions(desktopModeEventLogger)
- assertThat(transitionObserver.getLoggerSessionId()).isNull()
- }
-
- @Test
- fun sessionExitByRecents_cancelledAnimation_sessionRestored() {
- val sessionId = 1
- // add a freeform task to an existing session
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
- transitionObserver.setLoggerSessionId(sessionId)
-
- // recents transition sent freeform window to back
- val change = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
- val transitionInfo1 =
- TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
- .addChange(change)
- .build()
- callOnTransitionReady(transitionInfo1)
- verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
- verify(desktopModeEventLogger, times(1))
- .logSessionExit(eq(sessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW))
- assertThat(transitionObserver.getLoggerSessionId()).isNull()
-
- val transitionInfo2 = TransitionInfoBuilder(TRANSIT_NONE).build()
- callOnTransitionReady(transitionInfo2)
-
- verify(desktopModeEventLogger, times(1)).logSessionEnter(any(), any())
- verify(desktopModeEventLogger, times(1)).logTaskAdded(any(), any())
- }
-
- @Test
- fun sessionAlreadyStarted_newFreeformTaskAdded_logsTaskAdded() {
- val sessionId = 1
- // add an existing freeform task
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
- transitionObserver.setLoggerSessionId(sessionId)
-
- // new freeform task added
- val change = createChange(TRANSIT_OPEN, createTaskInfo(2, WINDOWING_MODE_FREEFORM))
- val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
- callOnTransitionReady(transitionInfo)
-
- verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
- verify(desktopModeEventLogger, never()).logSessionEnter(any(), any())
- }
-
- @Test
- fun sessionAlreadyStarted_freeformTaskRemoved_logsTaskRemoved() {
- val sessionId = 1
- // add two existing freeform tasks
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
- transitionObserver.addTaskInfosToCachedMap(createTaskInfo(2, WINDOWING_MODE_FREEFORM))
- transitionObserver.setLoggerSessionId(sessionId)
-
- // new freeform task added
- val change = createChange(TRANSIT_CLOSE, createTaskInfo(2, WINDOWING_MODE_FREEFORM))
- val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE, 0).addChange(change).build()
- callOnTransitionReady(transitionInfo)
-
- verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
- verify(desktopModeEventLogger, never()).logSessionExit(any(), any())
- }
-
- /** Simulate calling the onTransitionReady() method */
- private fun callOnTransitionReady(transitionInfo: TransitionInfo) {
- val transition = mock(IBinder::class.java)
- val startT = mock(SurfaceControl.Transaction::class.java)
- val finishT = mock(SurfaceControl.Transaction::class.java)
-
- transitionObserver.onTransitionReady(transition, transitionInfo, startT, finishT)
- }
-
- companion object {
- fun createTaskInfo(taskId: Int, windowMode: Int): ActivityManager.RunningTaskInfo {
- val taskInfo = ActivityManager.RunningTaskInfo()
- taskInfo.taskId = taskId
- taskInfo.configuration.windowConfiguration.windowingMode = windowMode
-
- return taskInfo
- }
-
- fun createChange(mode: Int, taskInfo: ActivityManager.RunningTaskInfo): Change {
- val change =
- Change(
- WindowContainerToken(mock(IWindowContainerToken::class.java)),
- mock(SurfaceControl::class.java)
- )
- change.mode = mode
- change.taskInfo = taskInfo
- return change
- }
- }
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index 56c4cea..e291c0e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -113,6 +113,8 @@
private DisplayInsetsController mDisplayInsetsController;
@Mock
private IRecentTasksListener mRecentTasksListener;
+ @Mock
+ private TaskStackTransitionObserver mTaskStackTransitionObserver;
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -139,7 +141,8 @@
mDisplayInsetsController, mMainExecutor));
mRecentTasksControllerReal = new RecentTasksController(mContext, mShellInit,
mShellController, mShellCommandHandler, mTaskStackListener, mActivityTaskManager,
- Optional.of(mDesktopModeTaskRepository), mMainExecutor);
+ Optional.of(mDesktopModeTaskRepository), mTaskStackTransitionObserver,
+ mMainExecutor);
mRecentTasksController = spy(mRecentTasksControllerReal);
mShellTaskOrganizer = new ShellTaskOrganizer(mShellInit, mShellCommandHandler,
null /* sizeCompatUI */, Optional.empty(), Optional.of(mRecentTasksController),
@@ -557,6 +560,30 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
+ public void onTaskMovedToFront_TaskStackObserverEnabled_triggersOnTaskMovedToFront()
+ throws Exception {
+ mRecentTasksControllerReal.registerRecentTasksListener(mRecentTasksListener);
+ ActivityManager.RunningTaskInfo taskInfo = makeRunningTaskInfo(/* taskId= */10);
+
+ mRecentTasksControllerReal.onTaskMovedToFrontThroughTransition(taskInfo);
+
+ verify(mRecentTasksListener).onTaskMovedToFront(taskInfo);
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
+ public void onTaskMovedToFront_TaskStackObserverEnabled_doesNotTriggersOnTaskMovedToFront()
+ throws Exception {
+ mRecentTasksControllerReal.registerRecentTasksListener(mRecentTasksListener);
+ ActivityManager.RunningTaskInfo taskInfo = makeRunningTaskInfo(/* taskId= */10);
+
+ mRecentTasksControllerReal.onTaskMovedToFront(taskInfo);
+
+ verify(mRecentTasksListener, never()).onTaskMovedToFront(any());
+ }
+
+ @Test
public void getNullSplitBoundsNonSplitTask() {
SplitBounds sb = mRecentTasksController.getSplitBoundsForTaskId(3);
assertNull("splitBounds should be null for non-split task", sb);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt
new file mode 100644
index 0000000..f959970
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.recents
+
+import android.app.ActivityManager
+import android.app.WindowConfiguration
+import android.os.IBinder
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+import android.testing.AndroidTestingRunner
+import android.view.SurfaceControl
+import android.view.WindowManager
+import android.window.IWindowContainerToken
+import android.window.TransitionInfo
+import android.window.WindowContainerToken
+import androidx.test.filters.SmallTest
+import com.android.window.flags.Flags
+import com.android.wm.shell.TestShellExecutor
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.transition.TransitionInfoBuilder
+import com.android.wm.shell.transition.Transitions
+import com.google.common.truth.Truth.assertThat
+import dagger.Lazy
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.same
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+
+/**
+ * Test class for {@link TaskStackTransitionObserver}
+ *
+ * Usage: atest WMShellUnitTests:TaskStackTransitionObserverTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class TaskStackTransitionObserverTest {
+
+ @JvmField @Rule val setFlagsRule = SetFlagsRule()
+
+ @Mock private lateinit var shellInit: ShellInit
+ @Mock lateinit var testExecutor: ShellExecutor
+ @Mock private lateinit var transitionsLazy: Lazy<Transitions>
+ @Mock private lateinit var transitions: Transitions
+ @Mock private lateinit var mockTransitionBinder: IBinder
+
+ private lateinit var transitionObserver: TaskStackTransitionObserver
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ shellInit = Mockito.spy(ShellInit(testExecutor))
+ whenever(transitionsLazy.get()).thenReturn(transitions)
+ transitionObserver = TaskStackTransitionObserver(transitionsLazy, shellInit)
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ val initRunnableCaptor = ArgumentCaptor.forClass(Runnable::class.java)
+ verify(shellInit)
+ .addInitCallback(initRunnableCaptor.capture(), same(transitionObserver))
+ initRunnableCaptor.value.run()
+ } else {
+ transitionObserver.onInit()
+ }
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
+ fun testRegistersObserverAtInit() {
+ verify(transitions).registerObserver(same(transitionObserver))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
+ fun taskCreated_freeformWindow_listenerNotified() {
+ val listener = TestListener()
+ val executor = TestShellExecutor()
+ transitionObserver.addTaskStackTransitionObserverListener(listener, executor)
+ val change =
+ createChange(
+ WindowManager.TRANSIT_OPEN,
+ createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FREEFORM)
+ )
+ val transitionInfo =
+ TransitionInfoBuilder(WindowManager.TRANSIT_OPEN, 0).addChange(change).build()
+
+ callOnTransitionReady(transitionInfo)
+ callOnTransitionFinished()
+ executor.flushAll()
+
+ assertThat(listener.taskInfoToBeNotified.taskId).isEqualTo(change.taskInfo?.taskId)
+ assertThat(listener.taskInfoToBeNotified.windowingMode)
+ .isEqualTo(change.taskInfo?.windowingMode)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
+ fun taskCreated_fullscreenWindow_listenerNotNotified() {
+ val listener = TestListener()
+ val executor = TestShellExecutor()
+ transitionObserver.addTaskStackTransitionObserverListener(listener, executor)
+ val change =
+ createChange(
+ WindowManager.TRANSIT_OPEN,
+ createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FULLSCREEN)
+ )
+ val transitionInfo =
+ TransitionInfoBuilder(WindowManager.TRANSIT_OPEN, 0).addChange(change).build()
+
+ callOnTransitionReady(transitionInfo)
+ callOnTransitionFinished()
+ executor.flushAll()
+
+ assertThat(listener.taskInfoToBeNotified.taskId).isEqualTo(0)
+ assertThat(listener.taskInfoToBeNotified.windowingMode)
+ .isEqualTo(WindowConfiguration.WINDOWING_MODE_UNDEFINED)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
+ fun taskCreated_freeformWindowOnTopOfFreeform_listenerNotified() {
+ val listener = TestListener()
+ val executor = TestShellExecutor()
+ transitionObserver.addTaskStackTransitionObserverListener(listener, executor)
+ val freeformOpenChange =
+ createChange(
+ WindowManager.TRANSIT_OPEN,
+ createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FREEFORM)
+ )
+ val freeformReorderChange =
+ createChange(
+ WindowManager.TRANSIT_TO_BACK,
+ createTaskInfo(2, WindowConfiguration.WINDOWING_MODE_FREEFORM)
+ )
+ val transitionInfo =
+ TransitionInfoBuilder(WindowManager.TRANSIT_OPEN, 0)
+ .addChange(freeformOpenChange)
+ .addChange(freeformReorderChange)
+ .build()
+
+ callOnTransitionReady(transitionInfo)
+ callOnTransitionFinished()
+ executor.flushAll()
+
+ assertThat(listener.taskInfoToBeNotified.taskId)
+ .isEqualTo(freeformOpenChange.taskInfo?.taskId)
+ assertThat(listener.taskInfoToBeNotified.windowingMode)
+ .isEqualTo(freeformOpenChange.taskInfo?.windowingMode)
+ }
+
+ class TestListener : TaskStackTransitionObserver.TaskStackTransitionObserverListener {
+ var taskInfoToBeNotified = ActivityManager.RunningTaskInfo()
+
+ override fun onTaskMovedToFrontThroughTransition(
+ taskInfo: ActivityManager.RunningTaskInfo
+ ) {
+ taskInfoToBeNotified = taskInfo
+ }
+ }
+
+ /** Simulate calling the onTransitionReady() method */
+ private fun callOnTransitionReady(transitionInfo: TransitionInfo) {
+ val startT = Mockito.mock(SurfaceControl.Transaction::class.java)
+ val finishT = Mockito.mock(SurfaceControl.Transaction::class.java)
+
+ transitionObserver.onTransitionReady(mockTransitionBinder, transitionInfo, startT, finishT)
+ }
+
+ /** Simulate calling the onTransitionFinished() method */
+ private fun callOnTransitionFinished() {
+ transitionObserver.onTransitionFinished(mockTransitionBinder, false)
+ }
+
+ companion object {
+ fun createTaskInfo(taskId: Int, windowingMode: Int): ActivityManager.RunningTaskInfo {
+ val taskInfo = ActivityManager.RunningTaskInfo()
+ taskInfo.taskId = taskId
+ taskInfo.configuration.windowConfiguration.windowingMode = windowingMode
+
+ return taskInfo
+ }
+
+ fun createChange(
+ mode: Int,
+ taskInfo: ActivityManager.RunningTaskInfo
+ ): TransitionInfo.Change {
+ val change =
+ TransitionInfo.Change(
+ WindowContainerToken(Mockito.mock(IWindowContainerToken::class.java)),
+ Mockito.mock(SurfaceControl::class.java)
+ )
+ change.mode = mode
+ change.taskInfo = taskInfo
+ return change
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index 1b223cf..46c1589 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -23,12 +23,15 @@
import static android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.wm.shell.MockSurfaceControlHelper.createMockSurfaceControlTransaction;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -37,7 +40,7 @@
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
-import android.content.res.Configuration;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.os.Handler;
@@ -47,13 +50,19 @@
import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
+import android.view.AttachedSurfaceControl;
import android.view.Choreographer;
import android.view.Display;
+import android.view.GestureDetector;
+import android.view.InsetsState;
+import android.view.MotionEvent;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
+import android.view.View;
import android.view.WindowManager;
import android.window.WindowContainerTransaction;
+import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
import com.android.dx.mockito.inline.extended.StaticMockitoSession;
@@ -74,6 +83,7 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.quality.Strictness;
@@ -112,18 +122,25 @@
@Mock
private Supplier<SurfaceControl.Transaction> mMockTransactionSupplier;
@Mock
- private SurfaceControl.Transaction mMockTransaction;
- @Mock
private SurfaceControl mMockSurfaceControl;
@Mock
private SurfaceControlViewHost mMockSurfaceControlViewHost;
@Mock
+ private AttachedSurfaceControl mMockRootSurfaceControl;
+ @Mock
private WindowDecoration.SurfaceControlViewHostFactory mMockSurfaceControlViewHostFactory;
@Mock
private TypedArray mMockRoundedCornersRadiusArray;
- private final Configuration mConfiguration = new Configuration();
+ @Mock
+ private TestTouchEventListener mMockTouchEventListener;
+ @Mock
+ private DesktopModeWindowDecoration.ExclusionRegionListener mMockExclusionRegionListener;
+ @Mock
+ private PackageManager mMockPackageManager;
+ private final InsetsState mInsetsState = new InsetsState();
+ private SurfaceControl.Transaction mMockTransaction;
private StaticMockitoSession mMockitoSession;
private TestableContext mTestableContext;
@@ -145,9 +162,17 @@
when(DesktopModeStatus.useDesktopOverrideDensity()).thenReturn(false);
doReturn(mMockSurfaceControlViewHost).when(mMockSurfaceControlViewHostFactory).create(
any(), any(), any());
+ when(mMockSurfaceControlViewHost.getRootSurfaceControl())
+ .thenReturn(mMockRootSurfaceControl);
+ mMockTransaction = createMockSurfaceControlTransaction();
doReturn(mMockTransaction).when(mMockTransactionSupplier).get();
mTestableContext = new TestableContext(mContext);
mTestableContext.ensureTestableResources();
+ mContext.setMockPackageManager(mMockPackageManager);
+ when(mMockPackageManager.getApplicationLabel(any())).thenReturn("applicationLabel");
+ final Display defaultDisplay = mock(Display.class);
+ doReturn(defaultDisplay).when(mMockDisplayController).getDisplay(Display.DEFAULT_DISPLAY);
+ doReturn(mInsetsState).when(mMockDisplayController).getInsetsState(anyInt());
}
@After
@@ -341,6 +366,99 @@
assertThat(hasNoInputChannelFeature(relayoutParams)).isTrue();
}
+ @Test
+ public void relayout_fullscreenTask_appliesTransactionImmediately() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+ spyWindowDecor.relayout(taskInfo);
+
+ verify(mMockTransaction).apply();
+ verify(mMockRootSurfaceControl, never()).applyTransactionOnDraw(any());
+ }
+
+ @Test
+ public void relayout_freeformTask_appliesTransactionOnDraw() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ // Make non-resizable to avoid dealing with input-permissions (MONITOR_INPUT)
+ taskInfo.isResizeable = false;
+
+ spyWindowDecor.relayout(taskInfo);
+
+ verify(mMockTransaction, never()).apply();
+ verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockTransaction);
+ }
+
+ @Test
+ public void relayout_fullscreenTask_doesNotCreateViewHostImmediately() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+ spyWindowDecor.relayout(taskInfo);
+
+ verify(mMockSurfaceControlViewHostFactory, never()).create(any(), any(), any());
+ }
+
+ @Test
+ public void relayout_fullscreenTask_postsViewHostCreation() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+ ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
+ spyWindowDecor.relayout(taskInfo);
+
+ verify(mMockHandler).post(runnableArgument.capture());
+ runnableArgument.getValue().run();
+ verify(mMockSurfaceControlViewHostFactory).create(any(), any(), any());
+ }
+
+ @Test
+ public void relayout_freeformTask_createsViewHostImmediately() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ // Make non-resizable to avoid dealing with input-permissions (MONITOR_INPUT)
+ taskInfo.isResizeable = false;
+
+ spyWindowDecor.relayout(taskInfo);
+
+ verify(mMockSurfaceControlViewHostFactory).create(any(), any(), any());
+ verify(mMockHandler, never()).post(any());
+ }
+
+ @Test
+ public void relayout_removesExistingHandlerCallback() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
+ spyWindowDecor.relayout(taskInfo);
+ verify(mMockHandler).post(runnableArgument.capture());
+
+ spyWindowDecor.relayout(taskInfo);
+
+ verify(mMockHandler).removeCallbacks(runnableArgument.getValue());
+ }
+
+ @Test
+ public void close_removesExistingHandlerCallback() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
+ spyWindowDecor.relayout(taskInfo);
+ verify(mMockHandler).post(runnableArgument.capture());
+
+ spyWindowDecor.close();
+
+ verify(mMockHandler).removeCallbacks(runnableArgument.getValue());
+ }
+
private void fillRoundedCornersResources(int fillValue) {
when(mMockRoundedCornersRadiusArray.getDimensionPixelSize(anyInt(), anyInt()))
.thenReturn(fillValue);
@@ -361,12 +479,16 @@
private DesktopModeWindowDecoration createWindowDecoration(
ActivityManager.RunningTaskInfo taskInfo) {
- return new DesktopModeWindowDecoration(mContext, mMockDisplayController,
- mMockShellTaskOrganizer, taskInfo, mMockSurfaceControl,
+ DesktopModeWindowDecoration windowDecor = new DesktopModeWindowDecoration(mContext,
+ mMockDisplayController, mMockShellTaskOrganizer, taskInfo, mMockSurfaceControl,
mMockHandler, mMockChoreographer, mMockSyncQueue, mMockRootTaskDisplayAreaOrganizer,
SurfaceControl.Builder::new, mMockTransactionSupplier,
WindowContainerTransaction::new, SurfaceControl::new,
mMockSurfaceControlViewHostFactory);
+ windowDecor.setCaptionListeners(mMockTouchEventListener, mMockTouchEventListener,
+ mMockTouchEventListener, mMockTouchEventListener);
+ windowDecor.setExclusionRegionListener(mMockExclusionRegionListener);
+ return windowDecor;
}
private ActivityManager.RunningTaskInfo createTaskInfo(boolean visible) {
@@ -391,4 +513,32 @@
return (params.mInputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL)
!= 0;
}
+
+ private static class TestTouchEventListener extends GestureDetector.SimpleOnGestureListener
+ implements View.OnClickListener, View.OnTouchListener, View.OnLongClickListener,
+ View.OnGenericMotionListener, DragDetector.MotionEventHandler {
+
+ @Override
+ public void onClick(View v) {}
+
+ @Override
+ public boolean onGenericMotion(View v, MotionEvent event) {
+ return false;
+ }
+
+ @Override
+ public boolean onLongClick(View v) {
+ return false;
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ return false;
+ }
+
+ @Override
+ public boolean handleMotionEvent(@Nullable View v, MotionEvent ev) {
+ return false;
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
index e6fabcf..f750e6b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
@@ -16,14 +16,20 @@
package com.android.wm.shell.windowdecor
import android.app.ActivityManager
+import android.content.Context
+import android.content.res.Resources
import android.graphics.PointF
import android.graphics.Rect
import android.os.IBinder
+import android.platform.test.annotations.EnableFlags
import android.testing.AndroidTestingRunner
import android.view.Display
import android.window.WindowContainerToken
+import com.android.window.flags.Flags
+import com.android.wm.shell.R
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.shared.DesktopModeStatus
import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM
import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT
import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP
@@ -33,8 +39,8 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito.`when` as whenever
import org.mockito.Mockito.any
+import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
/**
@@ -57,6 +63,10 @@
private lateinit var mockDisplayLayout: DisplayLayout
@Mock
private lateinit var mockDisplay: Display
+ @Mock
+ private lateinit var mockContext: Context
+ @Mock
+ private lateinit var mockResources: Resources
@Before
fun setup() {
@@ -69,16 +79,15 @@
(i.arguments.first() as Rect).set(STABLE_BOUNDS)
}
- mockWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply {
- taskId = TASK_ID
- token = taskToken
- minWidth = MIN_WIDTH
- minHeight = MIN_HEIGHT
- defaultMinSize = DEFAULT_MIN
- displayId = DISPLAY_ID
- configuration.windowConfiguration.setBounds(STARTING_BOUNDS)
- }
+ initializeTaskInfo()
mockWindowDecoration.mDisplay = mockDisplay
+ mockWindowDecoration.mDecorWindowContext = mockContext
+ whenever(mockContext.getResources()).thenReturn(mockResources)
+ whenever(mockWindowDecoration.mDecorWindowContext.resources).thenReturn(mockResources)
+ whenever(mockResources.getDimensionPixelSize(R.dimen.desktop_mode_minimum_window_width))
+ .thenReturn(DESKTOP_MODE_MIN_WIDTH)
+ whenever(mockResources.getDimensionPixelSize(R.dimen.desktop_mode_minimum_window_height))
+ .thenReturn(DESKTOP_MODE_MIN_HEIGHT)
whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID }
}
@@ -93,8 +102,8 @@
val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP,
- repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta,
- mockDisplayController, mockWindowDecoration)
+ repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
+ mockWindowDecoration)
assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top)
@@ -113,8 +122,8 @@
val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP,
- repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta,
- mockDisplayController, mockWindowDecoration)
+ repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
+ mockWindowDecoration)
assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top + 5)
@@ -127,14 +136,14 @@
val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.top.toFloat())
val repositionTaskBounds = Rect(STARTING_BOUNDS)
- // Resize to width of 95px and width of -5px with minimum of 10px
+ // Resize to width of 95px and height of -5px with minimum of 10px
val newX = STARTING_BOUNDS.right.toFloat() - 5
val newY = STARTING_BOUNDS.top.toFloat() + 105
val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP,
- repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta,
- mockDisplayController, mockWindowDecoration)
+ repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
+ mockWindowDecoration)
assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top)
@@ -153,8 +162,8 @@
val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP,
- repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta,
- mockDisplayController, mockWindowDecoration)
+ repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
+ mockWindowDecoration)
assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top + 80)
assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right - 80)
@@ -172,8 +181,8 @@
val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP,
- repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta,
- mockDisplayController, mockWindowDecoration)
+ repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
+ mockWindowDecoration)
assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top)
assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right)
@@ -196,14 +205,13 @@
assertThat(repositionTaskBounds.left).isEqualTo(validDragArea.left)
assertThat(repositionTaskBounds.top).isEqualTo(validDragArea.bottom)
assertThat(repositionTaskBounds.right)
- .isEqualTo(validDragArea.left + STARTING_BOUNDS.width())
+ .isEqualTo(validDragArea.left + STARTING_BOUNDS.width())
assertThat(repositionTaskBounds.bottom)
- .isEqualTo(validDragArea.bottom + STARTING_BOUNDS.height())
+ .isEqualTo(validDragArea.bottom + STARTING_BOUNDS.height())
}
@Test
fun testChangeBounds_toDisallowedBounds_freezesAtLimit() {
- var hasMoved = false
val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(),
STARTING_BOUNDS.bottom.toFloat())
val repositionTaskBounds = Rect(STARTING_BOUNDS)
@@ -212,26 +220,127 @@
var newY = STARTING_BOUNDS.bottom.toFloat() + 10
var delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
assertTrue(DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
- repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta,
- mockDisplayController, mockWindowDecoration))
- hasMoved = true
+ repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
+ mockWindowDecoration))
// Resize width to 120px, height to disallowed area which should not result in a change.
newX += 10
newY = DISALLOWED_RESIZE_AREA.top.toFloat()
delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
assertTrue(DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
- repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta,
- mockDisplayController, mockWindowDecoration))
+ repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
+ mockWindowDecoration))
assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top)
assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right + 20)
assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom + 10)
}
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS)
+ fun taskMinWidthHeightUndefined_changeBoundsInDesktopModeLessThanMin_shouldNotChangeBounds() {
+ whenever(DesktopModeStatus.canEnterDesktopMode(mockContext)).thenReturn(true)
+ initializeTaskInfo(taskMinWidth = -1, taskMinHeight = -1)
+ val startingPoint =
+ PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat())
+ val repositionTaskBounds = Rect(STARTING_BOUNDS)
+ // Shrink height and width to 1px. The default allowed width and height are defined in
+ // R.dimen.desktop_mode_minimum_window_width and R.dimen.desktop_mode_minimum_window_height
+ val newX = STARTING_BOUNDS.right.toFloat() - 99
+ val newY = STARTING_BOUNDS.bottom.toFloat() - 99
+ val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
+
+ DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
+ repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
+ mockWindowDecoration)
+ assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
+ assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top)
+ assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right)
+ assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS)
+ fun taskMinWidthHeightUndefined_changeBoundsInDesktopModeAllowedSize_shouldChangeBounds() {
+ whenever(DesktopModeStatus.canEnterDesktopMode(mockContext)).thenReturn(true)
+ initializeTaskInfo(taskMinWidth = -1, taskMinHeight = -1)
+ val startingPoint =
+ PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat())
+ val repositionTaskBounds = Rect(STARTING_BOUNDS)
+ // Shrink height and width to 20px. The default allowed width and height are defined in
+ // R.dimen.desktop_mode_minimum_window_width and R.dimen.desktop_mode_minimum_window_height
+ val newX = STARTING_BOUNDS.right.toFloat() - 80
+ val newY = STARTING_BOUNDS.bottom.toFloat() - 80
+ val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
+
+ DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
+ repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
+ mockWindowDecoration)
+ assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
+ assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top)
+ assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right - 80)
+ assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom - 80)
+ }
+
+ @Test
+ fun taskMinWidthHeightUndefined_changeBoundsLessThanDefaultMinSize_shouldNotChangeBounds() {
+ initializeTaskInfo(taskMinWidth = -1, taskMinHeight = -1)
+ val startingPoint =
+ PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat())
+ val repositionTaskBounds = Rect(STARTING_BOUNDS)
+ // Shrink height and width to 1px. The default allowed width and height are defined in the
+ // defaultMinSize of the TaskInfo.
+ val newX = STARTING_BOUNDS.right.toFloat() - 99
+ val newY = STARTING_BOUNDS.bottom.toFloat() - 99
+ val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
+
+ DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
+ repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
+ mockWindowDecoration)
+ assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
+ assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top)
+ assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right)
+ assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom)
+ }
+
+ @Test
+ fun taskMinWidthHeightUndefined_changeBoundsToAnAllowedSize_shouldChangeBounds() {
+ initializeTaskInfo(taskMinWidth = -1, taskMinHeight = -1)
+ val startingPoint =
+ PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat())
+ val repositionTaskBounds = Rect(STARTING_BOUNDS)
+ // Shrink height and width to 50px. The default allowed width and height are defined in the
+ // defaultMinSize of the TaskInfo.
+ val newX = STARTING_BOUNDS.right.toFloat() - 50
+ val newY = STARTING_BOUNDS.bottom.toFloat() - 50
+ val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
+
+ DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
+ repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
+ mockWindowDecoration)
+ assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
+ assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top)
+ assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right - 50)
+ assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom - 50)
+ }
+
+ private fun initializeTaskInfo(taskMinWidth: Int = MIN_WIDTH, taskMinHeight: Int = MIN_HEIGHT) {
+ mockWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply {
+ taskId = TASK_ID
+ token = taskToken
+ minWidth = taskMinWidth
+ minHeight = taskMinHeight
+ defaultMinSize = DEFAULT_MIN
+ displayId = DISPLAY_ID
+ configuration.windowConfiguration.setBounds(STARTING_BOUNDS)
+ }
+ }
+
companion object {
private const val TASK_ID = 5
private const val MIN_WIDTH = 10
private const val MIN_HEIGHT = 10
+ private const val DESKTOP_MODE_MIN_WIDTH = 20
+ private const val DESKTOP_MODE_MIN_HEIGHT = 20
private const val DENSITY_DPI = 20
private const val DEFAULT_MIN = 40
private const val DISPLAY_ID = 1
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
index 9174556..6667504 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
@@ -2,6 +2,8 @@
import android.app.ActivityManager
import android.app.WindowConfiguration
+import android.content.Context
+import android.content.res.Resources
import android.graphics.Point
import android.graphics.Rect
import android.os.IBinder
@@ -17,6 +19,7 @@
import android.window.WindowContainerTransaction
import android.window.WindowContainerTransaction.Change.CHANGE_DRAG_RESIZING
import androidx.test.filters.SmallTest
+import com.android.wm.shell.R
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.DisplayController
@@ -83,7 +86,10 @@
private lateinit var mockTransaction: SurfaceControl.Transaction
@Mock
private lateinit var mockTransitionBinder: IBinder
-
+ @Mock
+ private lateinit var mockContext: Context
+ @Mock
+ private lateinit var mockResources: Resources
private lateinit var taskPositioner: FluidResizeTaskPositioner
@Before
@@ -119,6 +125,12 @@
}
`when`(mockWindowDecoration.calculateValidDragArea()).thenReturn(VALID_DRAG_AREA)
mockWindowDecoration.mDisplay = mockDisplay
+ mockWindowDecoration.mDecorWindowContext = mockContext
+ whenever(mockWindowDecoration.mDecorWindowContext.resources).thenReturn(mockResources)
+ whenever(mockResources.getDimensionPixelSize(R.dimen.desktop_mode_minimum_window_width))
+ .thenReturn(DESKTOP_MODE_MIN_WIDTH)
+ whenever(mockResources.getDimensionPixelSize(R.dimen.desktop_mode_minimum_window_height))
+ .thenReturn(DESKTOP_MODE_MIN_HEIGHT)
whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID }
whenever(mockTransitions.startTransition(anyInt(), any(), any()))
.doReturn(mockTransitionBinder)
@@ -788,6 +800,8 @@
private const val TASK_ID = 5
private const val MIN_WIDTH = 10
private const val MIN_HEIGHT = 10
+ private const val DESKTOP_MODE_MIN_WIDTH = 20
+ private const val DESKTOP_MODE_MIN_HEIGHT = 20
private const val DENSITY_DPI = 20
private const val DEFAULT_MIN = 40
private const val DISPLAY_ID = 1
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index e73069a..f3603e1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -32,6 +32,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.any;
@@ -828,6 +829,36 @@
eq(mMockTaskSurface), anyInt(), anyInt());
}
+ @Test
+ public void updateViewHost_applyTransactionOnDrawIsTrue_surfaceControlIsUpdated() {
+ final TestWindowDecoration windowDecor = createWindowDecoration(
+ new TestRunningTaskInfoBuilder().build());
+ mRelayoutParams.mApplyStartTransactionOnDraw = true;
+
+ windowDecor.updateViewHost(mRelayoutParams, mMockSurfaceControlStartT, mRelayoutResult);
+
+ verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockSurfaceControlStartT);
+ }
+
+ @Test
+ public void updateViewHost_nullDrawTransaction_applyTransactionOnDrawIsTrue_throwsException() {
+ final TestWindowDecoration windowDecor = createWindowDecoration(
+ new TestRunningTaskInfoBuilder().build());
+ mRelayoutParams.mApplyStartTransactionOnDraw = true;
+
+ assertThrows(IllegalArgumentException.class,
+ () -> windowDecor.updateViewHost(
+ mRelayoutParams, null /* onDrawTransaction */, mRelayoutResult));
+ }
+
+ @Test
+ public void updateViewHost_nullDrawTransaction_applyTransactionOnDrawIsFalse_doesNotThrow() {
+ final TestWindowDecoration windowDecor = createWindowDecoration(
+ new TestRunningTaskInfoBuilder().build());
+ mRelayoutParams.mApplyStartTransactionOnDraw = false;
+
+ windowDecor.updateViewHost(mRelayoutParams, null /* onDrawTransaction */, mRelayoutResult);
+ }
private TestWindowDecoration createWindowDecoration(ActivityManager.RunningTaskInfo taskInfo) {
return new TestWindowDecoration(mContext, mMockDisplayController, mMockShellTaskOrganizer,
diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp
index 07e97f8..a88139d 100644
--- a/libs/hwui/jni/Graphics.cpp
+++ b/libs/hwui/jni/Graphics.cpp
@@ -583,6 +583,16 @@
transferParams.a, transferParams.b, transferParams.c, transferParams.d,
transferParams.e, transferParams.f, transferParams.g);
+ // Some transfer functions that are considered valid by Skia are not
+ // accepted by android.graphics.
+ if (hasException(env)) {
+ // Callers (e.g. Bitmap#getColorSpace) are not expected to throw an
+ // Exception, so clear it and return null, which is a documented
+ // possibility.
+ env->ExceptionClear();
+ return nullptr;
+ }
+
jfloatArray xyzArray = env->NewFloatArray(9);
jfloat xyz[9] = {
xyzMatrix.vals[0][0],
diff --git a/location/lib/java/com/android/location/provider/SignificantPlaceProvider.java b/location/lib/java/com/android/location/provider/SignificantPlaceProvider.java
index 0b39a9a..df4b903 100644
--- a/location/lib/java/com/android/location/provider/SignificantPlaceProvider.java
+++ b/location/lib/java/com/android/location/provider/SignificantPlaceProvider.java
@@ -21,17 +21,22 @@
import android.hardware.location.ISignificantPlaceProvider;
import android.hardware.location.ISignificantPlaceProviderManager;
import android.os.Binder;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
+import android.util.Log;
import com.android.internal.annotations.GuardedBy;
/** @hide */
-public class SignificantPlaceProvider {
+public abstract class SignificantPlaceProvider {
public static final String ACTION = TrustManager.ACTION_BIND_SIGNIFICANT_PLACE_PROVIDER;
+ private static final String TAG = "SignificantPlaceProvider";
+
private final IBinder mBinder;
// write locked on mBinder, read lock is optional depending on atomicity requirements
@@ -69,6 +74,9 @@
}
}
+ /** Invoked when some client has checked whether the device is in a significant place. */
+ public abstract void onSignificantPlaceCheck();
+
private final class Service extends ISignificantPlaceProvider.Stub {
Service() {}
@@ -76,7 +84,7 @@
@Override
public void setSignificantPlaceProviderManager(ISignificantPlaceProviderManager manager) {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
- return;
+ throw new SecurityException();
}
synchronized (mBinder) {
@@ -91,5 +99,22 @@
mManager = manager;
}
}
+
+ @Override
+ public void onSignificantPlaceCheck() {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException();
+ }
+
+ try {
+ SignificantPlaceProvider.this.onSignificantPlaceCheck();
+ } catch (RuntimeException e) {
+ // exceptions on one-way binder threads are dropped - move to a different thread
+ Log.w(TAG, e);
+ new Handler(Looper.getMainLooper()).post(() -> {
+ throw new AssertionError(e);
+ });
+ }
+ }
}
}
diff --git a/media/java/android/media/tv/ITvInputService.aidl b/media/java/android/media/tv/ITvInputService.aidl
old mode 100755
new mode 100644
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
old mode 100755
new mode 100644
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
old mode 100755
new mode 100644
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
old mode 100755
new mode 100644
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
old mode 100755
new mode 100644
diff --git a/native/android/input.cpp b/native/android/input.cpp
index 0a22314..9a8cda3 100644
--- a/native/android/input.cpp
+++ b/native/android/input.cpp
@@ -100,7 +100,8 @@
}
int32_t AMotionEvent_getFlags(const AInputEvent* motion_event) {
- return static_cast<const MotionEvent*>(motion_event)->getFlags();
+ return static_cast<const MotionEvent*>(motion_event)->getFlags() &
+ ~AMOTION_EVENT_PRIVATE_FLAG_MASK;
}
int32_t AMotionEvent_getMetaState(const AInputEvent* motion_event) {
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index 02d72ad..44fa677 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -51,6 +51,9 @@
constexpr int64_t SEND_HINT_TIMEOUT = std::chrono::nanoseconds(100ms).count();
struct AWorkDuration : public hal::WorkDuration {};
+// Shared lock for the whole PerformanceHintManager and sessions
+static std::mutex sHintMutex = std::mutex{};
+
struct APerformanceHintManager {
public:
static APerformanceHintManager* getInstance();
@@ -192,6 +195,7 @@
}
auto out = new APerformanceHintSession(mHintManager, std::move(session), mPreferredRateNanos,
initialTargetWorkDurationNanos, sessionConfig);
+ std::scoped_lock lock(sHintMutex);
out->traceThreads(tids);
out->traceTargetDuration(initialTargetWorkDurationNanos);
out->tracePowerEfficient(false);
@@ -219,6 +223,7 @@
if (sessionConfig->id > INT32_MAX) {
ALOGE("Session ID too large, must fit 32-bit integer");
}
+ std::scoped_lock lock(sHintMutex);
constexpr int numEnums =
ndk::enum_range<hal::SessionHint>().end() - ndk::enum_range<hal::SessionHint>().begin();
mLastHintSentTimestamp = std::vector<int64_t>(numEnums, 0);
@@ -244,6 +249,7 @@
ret.getMessage());
return EPIPE;
}
+ std::scoped_lock lock(sHintMutex);
mTargetDurationNanos = targetDurationNanos;
/**
* Most of the workload is target_duration dependent, so now clear the cached samples
@@ -267,6 +273,7 @@
}
int APerformanceHintSession::sendHint(SessionHint hint) {
+ std::scoped_lock lock(sHintMutex);
if (hint < 0 || hint >= static_cast<int32_t>(mLastHintSentTimestamp.size())) {
ALOGE("%s: invalid session hint %d", __FUNCTION__, hint);
return EINVAL;
@@ -305,6 +312,7 @@
return EPIPE;
}
+ std::scoped_lock lock(sHintMutex);
traceThreads(tids);
return 0;
@@ -343,6 +351,7 @@
ret.getMessage());
return EPIPE;
}
+ std::scoped_lock lock(sHintMutex);
tracePowerEfficient(enabled);
return OK;
}
@@ -355,6 +364,7 @@
int64_t actualTotalDurationNanos = workDuration->durationNanos;
int64_t now = uptimeNanos();
workDuration->timeStampNanos = now;
+ std::scoped_lock lock(sHintMutex);
traceActualDuration(workDuration->durationNanos);
mActualWorkDurations.push_back(std::move(*workDuration));
diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp
index 8ea4632..746c280 100644
--- a/native/graphics/jni/Android.bp
+++ b/native/graphics/jni/Android.bp
@@ -111,6 +111,7 @@
"allocator_may_return_null = 1",
],
},
+ dictionary: "fuzz/imagedecoder_fuzzer.dict",
host_supported: true,
}
diff --git a/native/graphics/jni/fuzz/fuzz_imagedecoder.cpp b/native/graphics/jni/fuzz/fuzz_imagedecoder.cpp
index 838bf3f..6743997 100644
--- a/native/graphics/jni/fuzz/fuzz_imagedecoder.cpp
+++ b/native/graphics/jni/fuzz/fuzz_imagedecoder.cpp
@@ -15,32 +15,15 @@
*/
#include <android/imagedecoder.h>
-
#include <binder/IPCThreadState.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <cstdlib>
-#include <memory>
+#include <fuzzer/FuzzedDataProvider.h>
#ifdef PNG_MUTATOR_DEFINE_LIBFUZZER_CUSTOM_MUTATOR
#include <fuzz/png_mutator.h>
#endif
-struct DecoderDeleter {
- void operator()(AImageDecoder* decoder) const { AImageDecoder_delete(decoder); }
-};
-
-using DecoderPointer = std::unique_ptr<AImageDecoder, DecoderDeleter>;
-
-static DecoderPointer makeDecoder(const uint8_t* data, size_t size) {
- AImageDecoder* decoder = nullptr;
- int result = AImageDecoder_createFromBuffer(data, size, &decoder);
- if (result != ANDROID_IMAGE_DECODER_SUCCESS) {
- // This was not a valid image.
- return nullptr;
- }
- return DecoderPointer(decoder);
-}
+constexpr int32_t kMaxDimension = 5000;
+constexpr int32_t kMinDimension = 0;
struct PixelFreer {
void operator()(void* pixels) const { std::free(pixels); }
@@ -48,41 +31,113 @@
using PixelPointer = std::unique_ptr<void, PixelFreer>;
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- // Without this call, decoding HEIF may time out on binder IPC calls.
- android::ProcessState::self()->startThreadPool();
+AImageDecoder* init(const uint8_t* data, size_t size, bool useFileDescriptor) {
+ AImageDecoder* decoder = nullptr;
+ if (useFileDescriptor) {
+ constexpr char testFd[] = "tempFd";
+ int32_t fileDesc = open(testFd, O_RDWR | O_CREAT | O_TRUNC);
+ write(fileDesc, data, size);
+ AImageDecoder_createFromFd(fileDesc, &decoder);
+ close(fileDesc);
+ } else {
+ AImageDecoder_createFromBuffer(data, size, &decoder);
+ }
+ return decoder;
+}
- DecoderPointer decoder = makeDecoder(data, size);
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider dataProvider = FuzzedDataProvider(data, size);
+ /**
+ * Use maximum of 80% of buffer for creating decoder and save at least
+ * 20% buffer for fuzzing other APIs
+ */
+ const int32_t dataSize = dataProvider.ConsumeIntegralInRange<int32_t>(0, (size * 80) / 100);
+ std::vector<uint8_t> inputBuffer = dataProvider.ConsumeBytes<uint8_t>(dataSize);
+ AImageDecoder* decoder =
+ init(inputBuffer.data(), inputBuffer.size(), dataProvider.ConsumeBool());
if (!decoder) {
return 0;
}
-
- const AImageDecoderHeaderInfo* info = AImageDecoder_getHeaderInfo(decoder.get());
- int32_t width = AImageDecoderHeaderInfo_getWidth(info);
- int32_t height = AImageDecoderHeaderInfo_getHeight(info);
-
- // Set an arbitrary limit on the size of an image. The fuzzer runs with a
- // limited amount of memory, and keeping this allocation small allows the
- // fuzzer to continue running to try to find more serious problems. This
- // size is large enough to hold a photo taken by a current gen phone.
- constexpr int32_t kMaxDimension = 5000;
- if (width > kMaxDimension || height > kMaxDimension) {
- return 0;
+ const AImageDecoderHeaderInfo* headerInfo = AImageDecoder_getHeaderInfo(decoder);
+ AImageDecoderFrameInfo* frameInfo = AImageDecoderFrameInfo_create();
+ int32_t height = AImageDecoderHeaderInfo_getHeight(headerInfo);
+ int32_t width = AImageDecoderHeaderInfo_getWidth(headerInfo);
+ while (dataProvider.remaining_bytes()) {
+ auto invokeImageApi = dataProvider.PickValueInArray<const std::function<void()>>({
+ [&]() {
+ int32_t testHeight =
+ dataProvider.ConsumeIntegralInRange<int32_t>(kMinDimension,
+ kMaxDimension);
+ int32_t testWidth = dataProvider.ConsumeIntegralInRange<int32_t>(kMinDimension,
+ kMaxDimension);
+ int32_t result = AImageDecoder_setTargetSize(decoder, testHeight, testWidth);
+ if (result == ANDROID_IMAGE_DECODER_SUCCESS) {
+ height = testHeight;
+ width = testWidth;
+ }
+ },
+ [&]() {
+ const bool required = dataProvider.ConsumeBool();
+ AImageDecoder_setUnpremultipliedRequired(decoder, required);
+ },
+ [&]() {
+ AImageDecoder_setAndroidBitmapFormat(
+ decoder,
+ dataProvider.ConsumeIntegralInRange<
+ int32_t>(ANDROID_BITMAP_FORMAT_NONE,
+ ANDROID_BITMAP_FORMAT_RGBA_1010102) /* format */);
+ },
+ [&]() {
+ AImageDecoder_setDataSpace(decoder,
+ dataProvider
+ .ConsumeIntegral<int32_t>() /* dataspace */);
+ },
+ [&]() {
+ ARect rect{dataProvider.ConsumeIntegral<int32_t>() /* left */,
+ dataProvider.ConsumeIntegral<int32_t>() /* top */,
+ dataProvider.ConsumeIntegral<int32_t>() /* right */,
+ dataProvider.ConsumeIntegral<int32_t>() /* bottom */};
+ AImageDecoder_setCrop(decoder, rect);
+ },
+ [&]() { AImageDecoderHeaderInfo_getWidth(headerInfo); },
+ [&]() { AImageDecoderHeaderInfo_getMimeType(headerInfo); },
+ [&]() { AImageDecoderHeaderInfo_getAlphaFlags(headerInfo); },
+ [&]() { AImageDecoderHeaderInfo_getAndroidBitmapFormat(headerInfo); },
+ [&]() {
+ int32_t tempHeight;
+ int32_t tempWidth;
+ AImageDecoder_computeSampledSize(decoder,
+ dataProvider.ConsumeIntegral<
+ int>() /* sampleSize */,
+ &tempWidth, &tempHeight);
+ },
+ [&]() { AImageDecoderHeaderInfo_getDataSpace(headerInfo); },
+ [&]() { AImageDecoder_getRepeatCount(decoder); },
+ [&]() { AImageDecoder_getFrameInfo(decoder, frameInfo); },
+ [&]() { AImageDecoderFrameInfo_getDuration(frameInfo); },
+ [&]() { AImageDecoderFrameInfo_hasAlphaWithinBounds(frameInfo); },
+ [&]() { AImageDecoderFrameInfo_getDisposeOp(frameInfo); },
+ [&]() { AImageDecoderFrameInfo_getBlendOp(frameInfo); },
+ [&]() {
+ AImageDecoder_setInternallyHandleDisposePrevious(
+ decoder, dataProvider.ConsumeBool() /* handle */);
+ },
+ [&]() { AImageDecoder_rewind(decoder); },
+ [&]() { AImageDecoder_advanceFrame(decoder); },
+ [&]() {
+ size_t stride = AImageDecoder_getMinimumStride(decoder);
+ size_t pixelSize = height * stride;
+ auto pixels = PixelPointer(std::malloc(pixelSize));
+ if (!pixels.get()) {
+ return;
+ }
+ AImageDecoder_decodeImage(decoder, pixels.get(), stride, pixelSize);
+ },
+ });
+ invokeImageApi();
}
- size_t stride = AImageDecoder_getMinimumStride(decoder.get());
- size_t pixelSize = height * stride;
- auto pixels = PixelPointer(std::malloc(pixelSize));
- if (!pixels.get()) {
- return 0;
- }
-
- while (true) {
- int result = AImageDecoder_decodeImage(decoder.get(), pixels.get(), stride, pixelSize);
- if (result != ANDROID_IMAGE_DECODER_SUCCESS) break;
-
- result = AImageDecoder_advanceFrame(decoder.get());
- if (result != ANDROID_IMAGE_DECODER_SUCCESS) break;
- }
+ AImageDecoderFrameInfo_delete(frameInfo);
+ AImageDecoder_delete(decoder);
return 0;
}
diff --git a/native/graphics/jni/fuzz/imagedecoder_fuzzer.dict b/native/graphics/jni/fuzz/imagedecoder_fuzzer.dict
new file mode 100644
index 0000000..5b54a0e
--- /dev/null
+++ b/native/graphics/jni/fuzz/imagedecoder_fuzzer.dict
@@ -0,0 +1,7 @@
+kw1="\x89\x50\x4E\x47"
+kw2="\xff\xD8\xFF"
+kw4="\x52\x49\x46\x46"
+kw5="\x00\x00\x01\x00"
+kw6="\x47\x49\x46\x08"
+kw7="ftyp"
+kw8="\x04\x00\x00\x00"
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index 055ccbc..3375e18c 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -57,7 +57,9 @@
@FlaggedApi("android.nfc.nfc_oem_extension") public final class NfcOemExtension {
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void clearPreference();
+ method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void maybeTriggerFirmwareUpdate();
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcOemExtension.Callback);
+ method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void synchronizeScreenState();
method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void unregisterCallback(@NonNull android.nfc.NfcOemExtension.Callback);
}
diff --git a/nfc/java/android/nfc/INfcAdapter.aidl b/nfc/java/android/nfc/INfcAdapter.aidl
index 7150b54..fd77820 100644
--- a/nfc/java/android/nfc/INfcAdapter.aidl
+++ b/nfc/java/android/nfc/INfcAdapter.aidl
@@ -110,4 +110,6 @@
void registerOemExtensionCallback(INfcOemExtensionCallback callbacks);
void unregisterOemExtensionCallback(INfcOemExtensionCallback callbacks);
void clearPreference();
+ void setScreenState();
+ void checkFirmware();
}
diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java
index 1eff58c..f6138a6 100644
--- a/nfc/java/android/nfc/NfcOemExtension.java
+++ b/nfc/java/android/nfc/NfcOemExtension.java
@@ -141,6 +141,34 @@
}
}
+ /**
+ * Get the screen state from system and set it to current screen state.
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+ public void synchronizeScreenState() {
+ try {
+ NfcAdapter.sService.setScreenState();
+ } catch (RemoteException e) {
+ mAdapter.attemptDeadServiceRecovery(e);
+ }
+ }
+
+ /**
+ * Check if the firmware needs updating.
+ *
+ * <p>If an update is needed, a firmware will be triggered when NFC is disabled.
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+ @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+ public void maybeTriggerFirmwareUpdate() {
+ try {
+ NfcAdapter.sService.checkFirmware();
+ } catch (RemoteException e) {
+ mAdapter.attemptDeadServiceRecovery(e);
+ }
+ }
+
private final class NfcOemExtensionCallback extends INfcOemExtensionCallback.Stub {
@Override
public void onTagConnected(boolean connected, Tag tag) throws RemoteException {
diff --git a/packages/CrashRecovery/aconfig/flags.aconfig b/packages/CrashRecovery/aconfig/flags.aconfig
index 8cdef38..80412321 100644
--- a/packages/CrashRecovery/aconfig/flags.aconfig
+++ b/packages/CrashRecovery/aconfig/flags.aconfig
@@ -12,15 +12,22 @@
flag {
name: "enable_crashrecovery"
is_exported: true
- namespace: "crashrecovery"
+ namespace: "modularization"
description: "Enables various dependencies of crashrecovery module"
bug: "289203818"
}
flag {
name: "allow_rescue_party_flag_resets"
- namespace: "crashrecovery"
+ namespace: "modularization"
description: "Enables rescue party flag resets"
bug: "287618292"
is_fixed_read_only: true
}
+
+flag {
+ name: "reenable_settings_resets"
+ namespace: "modularization"
+ description: "Re-enables settings resets only, deletes flag resets"
+ bug: "333847376"
+}
diff --git a/packages/CredentialManager/res/values-pt-rBR/strings.xml b/packages/CredentialManager/res/values-pt-rBR/strings.xml
index bc8a969..91385c6 100644
--- a/packages/CredentialManager/res/values-pt-rBR/strings.xml
+++ b/packages/CredentialManager/res/values-pt-rBR/strings.xml
@@ -48,7 +48,7 @@
<string name="passwords" msgid="5419394230391253816">"senhas"</string>
<string name="sign_ins" msgid="4710739369149469208">"logins"</string>
<string name="sign_in_info" msgid="2627704710674232328">"informações de login"</string>
- <string name="save_credential_to_title" msgid="3172811692275634301">"Salvar <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> em"</string>
+ <string name="save_credential_to_title" msgid="3172811692275634301">"Salvar <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> no"</string>
<string name="create_passkey_in_other_device_title" msgid="2360053098931886245">"Criar chave de acesso em outro dispositivo?"</string>
<string name="save_password_on_other_device_title" msgid="5829084591948321207">"Salvar senha em outro dispositivo?"</string>
<string name="save_sign_in_on_other_device_title" msgid="2827990118560134692">"Salvar credenciais de login em outro dispositivo?"</string>
@@ -57,9 +57,9 @@
<string name="set_as_default" msgid="4415328591568654603">"Definir como padrão"</string>
<string name="settings" msgid="6536394145760913145">"Configurações"</string>
<string name="use_once" msgid="9027366575315399714">"Usar uma vez"</string>
- <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> senhas • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> chaves de acesso"</string>
- <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> senhas"</string>
- <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> chaves de acesso"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"Senhas (<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>) • Chaves de acesso (<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>)"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"Senhas (<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>)"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Chaves de acesso (<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>)"</string>
<string name="more_options_usage_credentials" msgid="1785697001787193984">"<xliff:g id="TOTALCREDENTIALSNUMBER">%1$s</xliff:g> credenciais"</string>
<string name="passkey_before_subtitle" msgid="2448119456208647444">"Chave de acesso"</string>
<string name="another_device" msgid="5147276802037801217">"Outro dispositivo"</string>
diff --git a/packages/CredentialManager/res/values-pt/strings.xml b/packages/CredentialManager/res/values-pt/strings.xml
index bc8a969..91385c6 100644
--- a/packages/CredentialManager/res/values-pt/strings.xml
+++ b/packages/CredentialManager/res/values-pt/strings.xml
@@ -48,7 +48,7 @@
<string name="passwords" msgid="5419394230391253816">"senhas"</string>
<string name="sign_ins" msgid="4710739369149469208">"logins"</string>
<string name="sign_in_info" msgid="2627704710674232328">"informações de login"</string>
- <string name="save_credential_to_title" msgid="3172811692275634301">"Salvar <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> em"</string>
+ <string name="save_credential_to_title" msgid="3172811692275634301">"Salvar <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> no"</string>
<string name="create_passkey_in_other_device_title" msgid="2360053098931886245">"Criar chave de acesso em outro dispositivo?"</string>
<string name="save_password_on_other_device_title" msgid="5829084591948321207">"Salvar senha em outro dispositivo?"</string>
<string name="save_sign_in_on_other_device_title" msgid="2827990118560134692">"Salvar credenciais de login em outro dispositivo?"</string>
@@ -57,9 +57,9 @@
<string name="set_as_default" msgid="4415328591568654603">"Definir como padrão"</string>
<string name="settings" msgid="6536394145760913145">"Configurações"</string>
<string name="use_once" msgid="9027366575315399714">"Usar uma vez"</string>
- <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> senhas • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> chaves de acesso"</string>
- <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> senhas"</string>
- <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> chaves de acesso"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"Senhas (<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>) • Chaves de acesso (<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>)"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"Senhas (<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>)"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Chaves de acesso (<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>)"</string>
<string name="more_options_usage_credentials" msgid="1785697001787193984">"<xliff:g id="TOTALCREDENTIALSNUMBER">%1$s</xliff:g> credenciais"</string>
<string name="passkey_before_subtitle" msgid="2448119456208647444">"Chave de acesso"</string>
<string name="another_device" msgid="5147276802037801217">"Outro dispositivo"</string>
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index 0da32bd..0b40d11 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -71,8 +71,6 @@
import java.util.ArrayList
import java.util.Objects
import java.util.concurrent.Executors
-import org.json.JSONException
-import org.json.JSONObject
class CredentialAutofillService : AutofillService() {
@@ -81,13 +79,6 @@
private const val SESSION_ID_KEY = "autofill_session_id"
private const val REQUEST_ID_KEY = "autofill_request_id"
- private const val CRED_HINT_PREFIX = "credential="
- private const val REQUEST_DATA_KEY = "requestData"
- private const val CANDIDATE_DATA_KEY = "candidateQueryData"
- private const val SYS_PROVIDER_REQ_KEY = "isSystemProviderRequired"
- private const val CRED_OPTIONS_KEY = "credentialOptions"
- private const val TYPE_KEY = "type"
- private const val REQ_TYPE_KEY = "get"
}
override fun onFillRequest(
@@ -740,7 +731,6 @@
uniqueAutofillIdsForRequest: MutableSet<AutofillId>
) {
val traversedViewNodes: MutableSet<AutofillId> = mutableSetOf()
- val credentialOptionsFromHints: MutableMap<String, CredentialOption> = mutableMapOf()
val windowNodes: List<AssistStructure.WindowNode> =
structure.run {
(0 until windowNodeCount).map { getWindowNodeAt(it) }
@@ -749,7 +739,7 @@
windowNodes.forEach { windowNode: AssistStructure.WindowNode ->
traverseNodeForRequest(
windowNode.rootViewNode, cmRequests, responseClientState, traversedViewNodes,
- credentialOptionsFromHints, sessionId, uniqueAutofillIdsForRequest)
+ sessionId, uniqueAutofillIdsForRequest)
}
}
@@ -758,7 +748,6 @@
cmRequests: MutableList<CredentialOption>,
responseClientState: Bundle,
traversedViewNodes: MutableSet<AutofillId>,
- credentialOptionsFromHints: MutableMap<String, CredentialOption>,
sessionId: Int,
uniqueAutofillIdsForRequest: MutableSet<AutofillId>
) {
@@ -769,9 +758,8 @@
responseClientState.putBoolean(
WEBVIEW_REQUESTED_CREDENTIAL_KEY, true)
}
- cmRequests.addAll(getCredentialOptionsFromViewNode(viewNode, it, responseClientState,
- traversedViewNodes, credentialOptionsFromHints, sessionId,
- uniqueAutofillIdsForRequest)
+ cmRequests.addAll(getCredentialOptionsFromViewNode(viewNode, traversedViewNodes,
+ sessionId, uniqueAutofillIdsForRequest)
)
traversedViewNodes.add(it)
}
@@ -783,18 +771,15 @@
children.forEach { childNode: AssistStructure.ViewNode ->
traverseNodeForRequest(
- childNode, cmRequests, responseClientState, traversedViewNodes,
- credentialOptionsFromHints, sessionId, uniqueAutofillIdsForRequest
+ childNode, cmRequests, responseClientState, traversedViewNodes, sessionId,
+ uniqueAutofillIdsForRequest
)
}
}
private fun getCredentialOptionsFromViewNode(
viewNode: AssistStructure.ViewNode,
- autofillId: AutofillId,
- responseClientState: Bundle,
traversedViewNodes: MutableSet<AutofillId>,
- credentialOptionsFromHints: MutableMap<String, CredentialOption>,
sessionId: Int,
uniqueAutofillIdsForRequest: MutableSet<AutofillId>
): MutableList<CredentialOption> {
@@ -830,85 +815,6 @@
}
}
}
- // TODO(b/325502552): clean up cred option logic in autofill hint
- val credentialHints: MutableList<String> = mutableListOf()
-
- if (viewNode.autofillHints != null) {
- for (hint in viewNode.autofillHints!!) {
- if (hint.startsWith(CRED_HINT_PREFIX)) {
- credentialHints.add(hint.substringAfter(CRED_HINT_PREFIX))
- if (viewNode.webDomain != null) {
- responseClientState.putBoolean(WEBVIEW_REQUESTED_CREDENTIAL_KEY, true)
- }
- }
- }
- }
-
- for (credentialHint in credentialHints) {
- try {
- convertJsonToCredentialOption(
- credentialHint, autofillId, credentialOptionsFromHints)
- .let { credentialOptions.addAll(it) }
- } catch (e: JSONException) {
- Log.i(TAG, "Exception while parsing response: " + e.message)
- }
- }
return credentialOptions
}
-
- private fun convertJsonToCredentialOption(
- jsonString: String,
- autofillId: AutofillId,
- credentialOptionsFromHints: MutableMap<String, CredentialOption>
- ): List<CredentialOption> {
- val credentialOptions: MutableList<CredentialOption> = mutableListOf()
-
- val json = JSONObject(jsonString)
- val jsonGet = json.getJSONObject(REQ_TYPE_KEY)
- val options = jsonGet.getJSONArray(CRED_OPTIONS_KEY)
- for (i in 0 until options.length()) {
- val option = options.getJSONObject(i)
- val optionString = option.toString()
- credentialOptionsFromHints[optionString]
- ?.let { credentialOption ->
- // if the current credential option was seen before, add the current
- // viewNode to the credential option, but do not add it to the option list
- // again. This will result in the same result as deduping based on
- // traversed viewNode.
- credentialOption.candidateQueryData.getParcelableArrayList(
- CredentialProviderService.EXTRA_AUTOFILL_ID, AutofillId::class.java)
- ?.let {
- it.add(autofillId)
- credentialOption.candidateQueryData.putParcelableArrayList(
- CredentialProviderService.EXTRA_AUTOFILL_ID, it)
- }
- } ?: run {
- val candidateBundle = convertJsonToBundle(option.getJSONObject(CANDIDATE_DATA_KEY))
- candidateBundle.putParcelableArrayList(
- CredentialProviderService.EXTRA_AUTOFILL_ID,
- arrayListOf(autofillId))
- val credentialOption = CredentialOption(
- option.getString(TYPE_KEY),
- convertJsonToBundle(option.getJSONObject(REQUEST_DATA_KEY)),
- candidateBundle,
- option.getBoolean(SYS_PROVIDER_REQ_KEY),
- )
- credentialOptions.add(credentialOption)
- credentialOptionsFromHints[optionString] = credentialOption
- }
- }
- return credentialOptions
- }
-
- private fun convertJsonToBundle(json: JSONObject): Bundle {
- val result = Bundle()
- json.keys().forEach {
- val v = json.get(it)
- when (v) {
- is String -> result.putString(it, v)
- is Boolean -> result.putBoolean(it, v)
- }
- }
- return result
- }
}
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt
index f65a1b7..c48e7e4 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt
@@ -71,7 +71,7 @@
},
scrimColor = MaterialTheme.colorScheme.scrim.copy(alpha = .32f),
shape = EntryShape.TopRoundedCorner,
- windowInsets = WindowInsets.navigationBars,
+ contentWindowInsets = { WindowInsets.navigationBars },
dragHandle = null,
// Never take over the full screen. We always want to leave some top scrim space
// for exiting and viewing the underlying app to help a user gain context.
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
index 652e62c..5728c8c 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -18,6 +18,7 @@
import android.content.Intent
import android.os.Bundle
+import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
@@ -33,6 +34,7 @@
@OptIn(ExperimentalHorologistApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
+ Log.d(TAG, "onCreate, intent: $intent")
super.onCreate(savedInstanceState)
setTheme(android.R.style.Theme_DeviceDefault)
setContent {
@@ -47,6 +49,7 @@
}
override fun onNewIntent(intent: Intent) {
+ Log.d(TAG, "onNewIntent, intent: $intent")
super.onNewIntent(intent)
setIntent(intent)
viewModel.updateRequest(intent)
diff --git a/packages/CtsShim/apk/arm/CtsShim.apk b/packages/CtsShim/apk/arm/CtsShim.apk
index 38a6d13..5ab190d 100644
--- a/packages/CtsShim/apk/arm/CtsShim.apk
+++ b/packages/CtsShim/apk/arm/CtsShim.apk
Binary files differ
diff --git a/packages/CtsShim/apk/arm/CtsShimPriv.apk b/packages/CtsShim/apk/arm/CtsShimPriv.apk
index bcd9836..51a8c46 100644
--- a/packages/CtsShim/apk/arm/CtsShimPriv.apk
+++ b/packages/CtsShim/apk/arm/CtsShimPriv.apk
Binary files differ
diff --git a/packages/CtsShim/apk/x86/CtsShim.apk b/packages/CtsShim/apk/x86/CtsShim.apk
index 38a6d13..5ab190d 100644
--- a/packages/CtsShim/apk/x86/CtsShim.apk
+++ b/packages/CtsShim/apk/x86/CtsShim.apk
Binary files differ
diff --git a/packages/CtsShim/apk/x86/CtsShimPriv.apk b/packages/CtsShim/apk/x86/CtsShimPriv.apk
index f778904..fcd0273 100644
--- a/packages/CtsShim/apk/x86/CtsShimPriv.apk
+++ b/packages/CtsShim/apk/x86/CtsShimPriv.apk
Binary files differ
diff --git a/packages/EasterEgg/AndroidManifest.xml b/packages/EasterEgg/AndroidManifest.xml
index 1500583..754abb2 100644
--- a/packages/EasterEgg/AndroidManifest.xml
+++ b/packages/EasterEgg/AndroidManifest.xml
@@ -33,7 +33,7 @@
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application
- android:icon="@drawable/android14_patch_adaptive"
+ android:icon="@drawable/android15_patch_adaptive"
android:label="@string/app_name">
<!-- Android V easter egg: Daydream version of Landroid
@@ -41,7 +41,7 @@
<service
android:name=".landroid.DreamUniverse"
android:exported="true"
- android:icon="@drawable/android14_patch_adaptive"
+ android:icon="@drawable/android15_patch_adaptive"
android:label="@string/v_egg_name"
android:description="@string/dream_description"
android:enabled="false"
@@ -62,7 +62,7 @@
android:name=".landroid.MainActivity"
android:exported="true"
android:label="@string/u_egg_name"
- android:icon="@drawable/android14_patch_adaptive"
+ android:icon="@drawable/android15_patch_adaptive"
android:configChanges="orientation|screenLayout|screenSize|density"
android:theme="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen">
<intent-filter>
diff --git a/packages/EasterEgg/res/drawable/android15_patch_adaptive.xml b/packages/EasterEgg/res/drawable/android15_patch_adaptive.xml
new file mode 100644
index 0000000..d949200
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/android15_patch_adaptive.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/android15_patch_adaptive_background"/>
+ <foreground android:drawable="@drawable/android15_patch_adaptive_foreground"/>
+ <monochrome android:drawable="@drawable/android15_patch_monochrome"/>
+</adaptive-icon>
diff --git a/packages/EasterEgg/res/drawable/android15_patch_adaptive_background.xml b/packages/EasterEgg/res/drawable/android15_patch_adaptive_background.xml
new file mode 100644
index 0000000..642b30a
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/android15_patch_adaptive_background.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108">
+ <!-- space -->
+ <path
+ android:pathData="M0,0h108v108h-108z"
+ android:fillColor="#202124"/>
+ <!-- stars -->
+ <group>
+ <path
+ android:pathData="M32,34 h1v1h-1z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M33,61 h1v1h-1z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M71,34 h1v1h-1z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M62,56 h1v1h-1z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M68,47 h1v1h-1z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M72,55 h1v1h-1z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M39,36 h1v1h-1z"
+ android:fillColor="#ffffff"/>
+
+ <path
+ android:pathData="M72,49 h2v2h-2z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M46,53 h2v2h-2z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M32,45 h2v2h-2z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M43,37 h2v2h-2z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M78,51 h2v2h-2z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M34,51 h2v2h-2z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M76,41 h2v2h-2z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M39,46 h2v2h-2z"
+ android:fillColor="#ffffff"/>
+ </group>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/android15_patch_adaptive_foreground.xml b/packages/EasterEgg/res/drawable/android15_patch_adaptive_foreground.xml
new file mode 100644
index 0000000..1100eb7
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/android15_patch_adaptive_foreground.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108">
+
+ <!-- zoomies -->
+ <group>
+ <path
+ android:pathData="M53,42C52.21,50.58 46.46,68.95 32.11,74.63C19.22,79.75 5.77,82.32 1.19,83.19C0.68,83.29 0.28,83.36 0,83.42V108H54H108V83.42C107.72,83.36 107.32,83.29 106.81,83.19C102.23,82.32 88.78,79.75 75.89,74.63C61.54,68.95 55.79,50.58 55,42H54H53Z"
+ android:fillColor="#C6FF00"
+ android:fillType="evenOdd"/>
+ <path
+ android:pathData="M53.25,42C52.88,50.53 50.44,69.01 43.68,74.67C36.91,80.33 32.65,82.86 31.37,83.41L54,102.87L76.63,83.41C75.35,82.86 71.09,80.33 64.32,74.67C57.56,69.01 55.12,50.53 54.75,42H54H53.25Z"
+ android:fillColor="#ffffff"
+ android:fillType="evenOdd"/>
+ <path
+ android:pathData="M54,42m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0"
+ android:fillColor="#ffffff"/>
+ </group>
+ <group>
+ <!-- head! it's like sputnik -->
+ <path
+ android:pathData="M54,94.25m-26.25,0a26.25,26.25 0,1 1,52.5 0a26.25,26.25 0,1 1,-52.5 0"
+ android:fillColor="#34A853"/>
+ <!-- ant -->
+ <path
+ android:pathData="M38,63.5L44.5,74.5"
+ android:strokeWidth="5"
+ android:fillColor="#00000000"
+ android:strokeColor="#34A853"
+ android:strokeLineCap="round"/>
+ <path
+ android:pathData="M70,63.5L63.5,74.5"
+ android:strokeWidth="5"
+ android:fillColor="#00000000"
+ android:strokeColor="#34A853"
+ android:strokeLineCap="round"/>
+ </group>
+ <!-- spaceship -->
+ <path
+ android:pathData="M54,34C52.34,34 51,35.29 51,36.88V40.44C51,40.75 51.25,41 51.56,41C51.87,41 52.13,40.75 52.13,40.44V39.48C52.13,38.87 52.63,38.37 53.25,38.37H54.75C55.37,38.37 55.87,38.87 55.87,39.48V40.44C55.87,40.75 56.13,41 56.44,41C56.75,41 57,40.75 57,40.44V36.88C57,35.29 55.66,34 54,34H54Z"
+ android:fillColor="#E9F3EB"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/android15_patch_monochrome.xml b/packages/EasterEgg/res/drawable/android15_patch_monochrome.xml
new file mode 100644
index 0000000..a91cc86
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/android15_patch_monochrome.xml
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108">
+ <group>
+ <path
+ android:pathData="
+ M54,94.25
+ m-26.25,0
+ a26.25,26.25 0,1 1,52.5 0
+ a26.25,26.25 0,1 1,-52.5 0
+ "
+ android:fillColor="#000000"/>
+ <path
+ android:pathData="M38,63.5L44.5,74.5"
+ android:strokeWidth="5"
+ android:fillColor="#00000000"
+ android:strokeColor="#000000"
+ android:strokeLineCap="round"/>
+ <path
+ android:pathData="M70,63.5L63.5,74.5"
+ android:strokeWidth="5"
+ android:fillColor="#00000000"
+ android:strokeColor="#000000"
+ android:strokeLineCap="round"/>
+
+ <path
+ android:pathData="
+ M54,34
+ C52.34,34 51,35.29 51,36.88
+ V40.44
+ C51,40.75 51.25,41 51.56,41
+ C51.87,41 52.13,40.75 52.13,40.44
+ V39.48
+ C52.13,38.87 52.63,38.37 53.25,38.37
+ H54.75
+ C55.37,38.37 55.87,38.87 55.87,39.48
+ V40.44
+ C55.87,40.75 56.13,41 56.44,41
+ C56.75,41 57,40.75 57,40.44
+ V36.88
+ C57,35.29 55.66,34 54,34
+ H54
+ Z
+ "
+ android:fillColor="#34A853"/>
+ <path
+ android:strokeWidth="1"
+ android:pathData="M54,40V67"
+ android:fillColor="#00000000"
+ android:strokeColor="#40FFFFFF"
+ />
+
+ <path
+ android:pathData="M32,34 h1v1h-1z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M33,61 h1v1h-1z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M71,34 h1v1h-1z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M62,56 h1v1h-1z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M68,47 h1v1h-1z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M72,55 h1v1h-1z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M39,36 h1v1h-1z"
+ android:fillColor="#ffffff"/>
+
+ <path
+ android:pathData="M72,49 h2v2h-2z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M46,53 h2v2h-2z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M32,45 h2v2h-2z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M43,37 h2v2h-2z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M78,51 h2v2h-2z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M34,51 h2v2h-2z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M76,41 h2v2h-2z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M39,46 h2v2h-2z"
+ android:fillColor="#ffffff"/>
+
+ </group>
+</vector>
diff --git a/packages/EasterEgg/src/com/android/egg/landroid/Physics.kt b/packages/EasterEgg/src/com/android/egg/landroid/Physics.kt
index fc66ad6..d14234e 100644
--- a/packages/EasterEgg/src/com/android/egg/landroid/Physics.kt
+++ b/packages/EasterEgg/src/com/android/egg/landroid/Physics.kt
@@ -20,7 +20,7 @@
import kotlin.random.Random
// artificially speed up or slow down the simulation
-const val TIME_SCALE = 1f
+const val TIME_SCALE = 1f // simulation seconds per wall clock second
// if it's been over 1 real second since our last timestep, don't simulate that elapsed time.
// this allows the simulation to "pause" when, for example, the activity pauses
@@ -36,6 +36,19 @@
fun postUpdate(sim: Simulator, dt: Float)
}
+interface Removable {
+ fun canBeRemoved(): Boolean
+}
+
+class Fuse(var lifetime: Float) : Removable {
+ fun update(dt: Float) {
+ lifetime -= dt
+ }
+ override fun canBeRemoved(): Boolean {
+ return lifetime < 0
+ }
+}
+
open class Body(var name: String = "Unknown") : Entity {
var pos = Vec2.Zero
var opos = Vec2.Zero
diff --git a/packages/EasterEgg/src/com/android/egg/landroid/Universe.kt b/packages/EasterEgg/src/com/android/egg/landroid/Universe.kt
index 1e54569..d6fbc11 100644
--- a/packages/EasterEgg/src/com/android/egg/landroid/Universe.kt
+++ b/packages/EasterEgg/src/com/android/egg/landroid/Universe.kt
@@ -43,11 +43,9 @@
const val MAIN_ENGINE_ACCEL = 1000f // thrust effect, pixels per second squared
const val LAUNCH_MECO = 2f // how long to suspend gravity when launching
-const val SCALED_THRUST = true
+const val LANDING_REMOVAL_TIME = 3600f // one hour of simulation time
-interface Removable {
- fun canBeRemoved(): Boolean
-}
+const val SCALED_THRUST = true
open class Planet(
val orbitCenter: Vec2,
@@ -321,7 +319,7 @@
//
(1..10).forEach {
Spark(
- lifetime = rng.nextFloatInRange(0.5f, 2f),
+ ttl = rng.nextFloatInRange(0.5f, 2f),
style = Spark.Style.DOT,
color = Color.White,
size = 1f
@@ -359,13 +357,22 @@
entities
.filterIsInstance<Removable>()
.filter(predicate = Removable::canBeRemoved)
- .filterIsInstance<Entity>()
- .forEach { remove(it) }
+ .forEach { remove(it as Entity) }
+
+ constraints
+ .filterIsInstance<Removable>()
+ .filter(predicate = Removable::canBeRemoved)
+ .forEach { remove(it as Constraint) }
}
}
-class Landing(var ship: Spacecraft?, val planet: Planet, val angle: Float, val text: String = "") :
- Constraint {
+class Landing(
+ var ship: Spacecraft?,
+ val planet: Planet,
+ val angle: Float,
+ val text: String = "",
+ private val fuse: Fuse = Fuse(LANDING_REMOVAL_TIME)
+) : Constraint, Removable by fuse {
override fun solve(sim: Simulator, dt: Float) {
ship?.let { ship ->
val landingVector = Vec2.makeWithAngleMag(angle, ship.radius + planet.radius)
@@ -373,17 +380,20 @@
ship.pos = (ship.pos * 0.5f) + (desiredPos * 0.5f) // @@@ FIXME
ship.angle = angle
}
+
+ fuse.update(dt)
}
}
class Spark(
- var lifetime: Float,
+ var ttl: Float,
collides: Boolean = false,
mass: Float = 0f,
val style: Style = Style.LINE,
val color: Color = Color.Gray,
- val size: Float = 2f
-) : Removable, Body() {
+ val size: Float = 2f,
+ val fuse: Fuse = Fuse(ttl)
+) : Removable by fuse, Body(name = "Spark") {
enum class Style {
LINE,
LINE_ABSOLUTE,
@@ -398,10 +408,7 @@
}
override fun update(sim: Simulator, dt: Float) {
super.update(sim, dt)
- lifetime -= dt
- }
- override fun canBeRemoved(): Boolean {
- return lifetime < 0
+ fuse.update(dt)
}
}
@@ -486,11 +493,11 @@
// exhaust
sim.add(
Spark(
- lifetime = sim.rng.nextFloatInRange(0.5f, 1f),
+ ttl = sim.rng.nextFloatInRange(0.5f, 1f),
collides = true,
mass = 1f,
style = Spark.Style.RING,
- size = 3f,
+ size = 1f,
color = Color(0x40FFFFFF)
)
.also { spark ->
diff --git a/packages/EasterEgg/src/com/android/egg/landroid/VisibleUniverse.kt b/packages/EasterEgg/src/com/android/egg/landroid/VisibleUniverse.kt
index 974784d..ed3ebc7 100644
--- a/packages/EasterEgg/src/com/android/egg/landroid/VisibleUniverse.kt
+++ b/packages/EasterEgg/src/com/android/egg/landroid/VisibleUniverse.kt
@@ -30,6 +30,7 @@
import androidx.core.math.MathUtils.clamp
import com.android.egg.flags.Flags.flagFlag
import java.lang.Float.max
+import kotlin.math.exp
import kotlin.math.sqrt
const val DRAW_ORBITS = true
@@ -289,7 +290,8 @@
fun ZoomedDrawScope.drawSpark(spark: Spark) {
with(spark) {
- if (lifetime < 0) return
+ if (fuse.lifetime < 0) return
+ val life = 1f - fuse.lifetime / ttl
when (style) {
Spark.Style.LINE ->
if (opos != Vec2.Zero) drawLine(color, opos, pos, strokeWidth = size)
@@ -297,7 +299,13 @@
if (opos != Vec2.Zero) drawLine(color, opos, pos, strokeWidth = size / zoom)
Spark.Style.DOT -> drawCircle(color, size, pos)
Spark.Style.DOT_ABSOLUTE -> drawCircle(color, size, pos / zoom)
- Spark.Style.RING -> drawCircle(color, size, pos, style = Stroke(width = 1f / zoom))
+ Spark.Style.RING ->
+ drawCircle(
+ color = color.copy(alpha = color.alpha * (1f - life)),
+ radius = exp(lerp(size, 3f * size, life)) - 1f,
+ center = pos,
+ style = Stroke(width = 1f / zoom)
+ )
}
}
}
diff --git a/packages/InputDevices/res/raw/keyboard_layout_serbian_and_montenegrin_cyrillic.kcm b/packages/InputDevices/res/raw/keyboard_layout_serbian_and_montenegrin_cyrillic.kcm
new file mode 100644
index 0000000..6fa54f9
--- /dev/null
+++ b/packages/InputDevices/res/raw/keyboard_layout_serbian_and_montenegrin_cyrillic.kcm
@@ -0,0 +1,320 @@
+# Copyright 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Serbian and Montenegrin (Cyrillic) keyboard layout.
+#
+
+type OVERLAY
+
+map key 86 PLUS
+
+### ROW 1
+
+key GRAVE {
+ label: '`'
+ base: '`'
+ shift: '~'
+}
+
+key 1 {
+ label: '1'
+ base: '1'
+ shift: '!'
+}
+
+key 2 {
+ label: '2'
+ base: '2'
+ shift: '\u0022'
+}
+
+key 3 {
+ label: '3'
+ base: '3'
+ shift: '#'
+}
+
+key 4 {
+ label: '4'
+ base: '4'
+ shift: '$'
+}
+
+key 5 {
+ label: '5'
+ base: '5'
+ shift: '%'
+}
+
+key 6 {
+ label: '6'
+ base: '6'
+ shift: '&'
+}
+
+key 7 {
+ label: '7'
+ base: '7'
+ shift: '/'
+}
+
+key 8 {
+ label: '8'
+ base: '8'
+ shift: '('
+}
+
+key 9 {
+ label: '9'
+ base: '9'
+ shift: ')'
+}
+
+key 0 {
+ label: '0'
+ base: '0'
+ shift: '='
+}
+
+key MINUS {
+ label: '\''
+ base: '\u030d'
+ shift: '?'
+}
+
+key EQUALS {
+ label: '+'
+ base: '+'
+ shift: '*'
+}
+
+### ROW 2
+
+key Q {
+ label: '\u0409'
+ base, capslock+shift: '\u0459'
+ shift, capslock: '\u0409'
+}
+
+key W {
+ label: '\u040a'
+ base, capslock+shift: '\u045a'
+ shift, capslock: '\u040a'
+}
+
+key E {
+ label: '\u0415'
+ base, capslock+shift: '\u0435'
+ shift, capslock: '\u0415'
+ ralt: '\u20ac'
+}
+
+key R {
+ label: '\u0420'
+ base, capslock+shift: '\u0440'
+ shift, capslock: '\u0420'
+}
+
+key T {
+ label: '\u0422'
+ base, capslock+shift: '\u0442'
+ shift, capslock: '\u0422'
+}
+
+key Y {
+ label: '\u0417'
+ base, capslock+shift: '\u0437'
+ shift, capslock: '\u0417'
+}
+
+key U {
+ label: '\u0423'
+ base, capslock+shift: '\u0443'
+ shift, capslock: '\u0423'
+}
+
+key I {
+ label: '\u0418'
+ base, capslock+shift: '\u0438'
+ shift, capslock: '\u0418'
+}
+
+key O {
+ label: '\u041e'
+ base, capslock+shift: '\u043e'
+ shift, capslock: '\u041e'
+}
+
+key P {
+ label: '\u041f'
+ base, capslock+shift: '\u043f'
+ shift, capslock: '\u041f'
+}
+
+key LEFT_BRACKET {
+ label: '\u0428'
+ base, capslock+shift: '\u0448'
+ shift, capslock: '\u0428'
+}
+
+key RIGHT_BRACKET {
+ label: '\u0402'
+ base, capslock+shift: '\u0452'
+ shift, capslock: '\u0402'
+}
+
+### ROW 3
+
+key A {
+ label: '\u0410'
+ base, capslock+shift: '\u0430'
+ shift, capslock: '\u0410'
+}
+
+key S {
+ label: '\u0421'
+ base, capslock+shift: '\u0441'
+ shift, capslock: '\u0421'
+}
+
+key D {
+ label: '\u0414'
+ base, capslock+shift: '\u0434'
+ shift, capslock: '\u0414'
+}
+
+key F {
+ label: '\u0424'
+ base, capslock+shift: '\u0444'
+ shift, capslock: '\u0424'
+}
+
+key G {
+ label: '\u0413'
+ base, capslock+shift: '\u0433'
+ shift, capslock: '\u0413'
+}
+
+key H {
+ label: '\u0425'
+ base, capslock+shift: '\u0445'
+ shift, capslock: '\u0425'
+}
+
+key J {
+ label: '\u0408'
+ base, capslock+shift: '\u0458'
+ shift, capslock: '\u0408'
+}
+
+key K {
+ label: '\u041a'
+ base, capslock+shift: '\u043a'
+ shift, capslock: '\u041a'
+}
+
+key L {
+ label: '\u041b'
+ base, capslock+shift: '\u043b'
+ shift, capslock: '\u041b'
+}
+
+key SEMICOLON {
+ label: '\u0427'
+ base, capslock+shift: '\u0447'
+ shift, capslock: '\u0427'
+}
+
+key APOSTROPHE {
+ label: '\u040b'
+ base, capslock+shift: '\u045b'
+ shift, capslock: '\u040b'
+}
+
+key BACKSLASH {
+ label: '\u0416'
+ base, capslock+shift: '\u0436'
+ shift, capslock: '\u0416'
+}
+
+### ROW 4
+
+key PLUS {
+ label: '<'
+ base: '<'
+ shift: '>'
+}
+
+key Z {
+ label: '\u0405'
+ base, capslock+shift: '\u0455'
+ shift, capslock: '\u0405'
+}
+
+key X {
+ label: '\u040f'
+ base, capslock+shift: '\u045f'
+ shift, capslock: '\u040f'
+}
+
+key C {
+ label: '\u0426'
+ base, capslock+shift: '\u0446'
+ shift, capslock: '\u0426'
+}
+
+key V {
+ label: '\u0412'
+ base, capslock+shift: '\u0432'
+ shift, capslock: '\u0412'
+}
+
+key B {
+ label: '\u0411'
+ base, capslock+shift: '\u0431'
+ shift, capslock: '\u0411'
+}
+
+key N {
+ label: '\u041d'
+ base, capslock+shift: '\u043d'
+ shift, capslock: '\u041d'
+}
+
+key M {
+ label: '\u041c'
+ base, capslock+shift: '\u043c'
+ shift, capslock: '\u041c'
+}
+
+key COMMA {
+ label: ','
+ base: ','
+ shift: ';'
+ ralt: '<'
+}
+
+key PERIOD {
+ label: '.'
+ base: '.'
+ shift: ':'
+ ralt: '>'
+}
+
+key SLASH {
+ label: '-'
+ base: '-'
+ shift: '_'
+}
\ No newline at end of file
diff --git a/packages/InputDevices/res/raw/keyboard_layout_serbian_latin.kcm b/packages/InputDevices/res/raw/keyboard_layout_serbian_and_montenegrin_latin.kcm
similarity index 99%
rename from packages/InputDevices/res/raw/keyboard_layout_serbian_latin.kcm
rename to packages/InputDevices/res/raw/keyboard_layout_serbian_and_montenegrin_latin.kcm
index 0ff1dea..8e4d7b1 100644
--- a/packages/InputDevices/res/raw/keyboard_layout_serbian_latin.kcm
+++ b/packages/InputDevices/res/raw/keyboard_layout_serbian_and_montenegrin_latin.kcm
@@ -13,7 +13,7 @@
# limitations under the License.
#
-# Serbian (Latin) keyboard layout.
+# Serbian and Montenegrin (Latin) keyboard layout.
#
type OVERLAY
diff --git a/packages/InputDevices/res/values/strings.xml b/packages/InputDevices/res/values/strings.xml
index be7d2c1..5a91125 100644
--- a/packages/InputDevices/res/values/strings.xml
+++ b/packages/InputDevices/res/values/strings.xml
@@ -158,4 +158,10 @@
<!-- Montenegrin (Latin) keyboard layout label. [CHAR LIMIT=35] -->
<string name="keyboard_layout_montenegrin_latin">Montenegrin (Latin)</string>
+
+ <!-- Serbian (Cyrillic) keyboard layout label. [CHAR LIMIT=35] -->
+ <string name="keyboard_layout_serbian_cyrillic">Serbian (Cyrillic)</string>
+
+ <!-- Montenegrin (Cyrillic) keyboard layout label. [CHAR LIMIT=35] -->
+ <string name="keyboard_layout_montenegrin_cyrillic">Montenegrin (Cyrillic)</string>
</resources>
diff --git a/packages/InputDevices/res/xml/keyboard_layouts.xml b/packages/InputDevices/res/xml/keyboard_layouts.xml
index 84e4b9e..9309489 100644
--- a/packages/InputDevices/res/xml/keyboard_layouts.xml
+++ b/packages/InputDevices/res/xml/keyboard_layouts.xml
@@ -336,14 +336,28 @@
<keyboard-layout
android:name="keyboard_layout_serbian_latin"
android:label="@string/keyboard_layout_serbian_latin"
- android:keyboardLayout="@raw/keyboard_layout_serbian_latin"
+ android:keyboardLayout="@raw/keyboard_layout_serbian_and_montenegrin_latin"
android:keyboardLocale="sr-Latn-RS"
android:keyboardLayoutType="qwertz" />
<keyboard-layout
android:name="keyboard_layout_montenegrin_latin"
android:label="@string/keyboard_layout_montenegrin_latin"
- android:keyboardLayout="@raw/keyboard_layout_serbian_latin"
+ android:keyboardLayout="@raw/keyboard_layout_serbian_and_montenegrin_latin"
android:keyboardLocale="cnr-Latn-ME"
android:keyboardLayoutType="qwertz" />
+
+ <keyboard-layout
+ android:name="keyboard_layout_serbian_cyrillic"
+ android:label="@string/keyboard_layout_serbian_cyrillic"
+ android:keyboardLayout="@raw/keyboard_layout_serbian_and_montenegrin_cyrillic"
+ android:keyboardLocale="sr-Cyrl-RS"
+ android:keyboardLayoutType="extended" />
+
+ <keyboard-layout
+ android:name="keyboard_layout_montenegrin_cyrillic"
+ android:label="@string/keyboard_layout_montenegrin_cyrillic"
+ android:keyboardLayout="@raw/keyboard_layout_serbian_and_montenegrin_cyrillic"
+ android:keyboardLocale="cnr-Cyrl-ME"
+ android:keyboardLayoutType="extended" />
</keyboard-layouts>
diff --git a/packages/PackageInstaller/res/values-pa/strings.xml b/packages/PackageInstaller/res/values-pa/strings.xml
index 1db92a0..80dc889 100644
--- a/packages/PackageInstaller/res/values-pa/strings.xml
+++ b/packages/PackageInstaller/res/values-pa/strings.xml
@@ -63,7 +63,7 @@
<string name="archive_application_text_all_users" msgid="3151229641681672580">"ਕੀ ਇਸ ਐਪ ਨੂੰ ਸਾਰੇ ਵਰਤੋਂਕਾਰਾਂ ਲਈ ਪੁਰਾਲੇਖਬੱਧ ਕਰਨਾ ਹੈ? ਤੁਹਾਡਾ ਨਿੱਜੀ ਡਾਟਾ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਵੇਗਾ"</string>
<string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"ਕੀ ਇਸ ਐਪ ਨੂੰ ਤੁਹਾਡੇ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ \'ਤੇ ਪੁਰਾਲੇਖਬੱਧ ਕਰਨਾ ਹੈ? ਤੁਹਾਡਾ ਨਿੱਜੀ ਡਾਟਾ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਵੇਗਾ"</string>
<string name="archive_application_text_user" msgid="2586558895535581451">"ਕੀ ਇਸ ਐਪ ਨੂੰ <xliff:g id="USERNAME">%1$s</xliff:g> ਲਈ ਪੁਰਾਲੇਖਬੱਧ ਕਰਨਾ ਹੈ? ਤੁਹਾਡਾ ਨਿੱਜੀ ਡਾਟਾ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਵੇਗਾ"</string>
- <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਆਪਣੀ ਨਿੱਜੀ ਸਪੇਸ ਤੋਂ ਪੁਰਾਲੇਖਬੱਧ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਤੁਹਾਡਾ ਨਿੱਜੀ ਡਾਟਾ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਵੇਗਾ"</string>
+ <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਆਪਣੀ ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ ਤੋਂ ਪੁਰਾਲੇਖਬੱਧ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਤੁਹਾਡਾ ਨਿੱਜੀ ਡਾਟਾ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਵੇਗਾ"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ "<b>"ਸਾਰੇ"</b>" ਵਰਤੋਂਕਾਰਾਂ ਲਈ ਅਣਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਐਪਲੀਕੇਸ਼ਨ ਅਤੇ ਇਸਦਾ ਡਾਟਾ ਡੀਵਾਈਸ \'ਤੇ "<b>"ਸਾਰੇ"</b>" ਵਰਤੋਂਕਾਰਾਂ ਵੱਲੋਂ ਹਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ।"</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"ਕੀ ਤੁਸੀਂ ਵਰਤੋਂਕਾਰ <xliff:g id="USERNAME">%1$s</xliff:g> ਲਈ ਇਸ ਐਪ ਨੂੰ ਅਣਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਆਪਣੇ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਤੋਂ ਅਣਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string>
diff --git a/packages/PackageInstaller/res/values-tr/strings.xml b/packages/PackageInstaller/res/values-tr/strings.xml
index 006ad52..e10eb0e 100644
--- a/packages/PackageInstaller/res/values-tr/strings.xml
+++ b/packages/PackageInstaller/res/values-tr/strings.xml
@@ -55,7 +55,7 @@
<string name="user_is_not_allowed_dlg_text" msgid="3468447791330611681">"Geçerli kullanıcının bu yüklemeyi kaldırma izni yok."</string>
<string name="generic_error_dlg_title" msgid="5863195085927067752">"Hata"</string>
<string name="generic_error_dlg_text" msgid="5287861443265795232">"Uygulamanın yüklemesi kaldırılamadı."</string>
- <string name="uninstall_application_title" msgid="4045420072401428123">"Uygulamanın yüklemesini kaldır"</string>
+ <string name="uninstall_application_title" msgid="4045420072401428123">"Uygulamayı kaldır"</string>
<string name="uninstall_update_title" msgid="824411791011583031">"Güncelleme kaldırılsın mı?"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>, şu uygulamanın bir parçasıdır:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"Bu uygulamanın yüklemesini kaldırmak istiyor musunuz?"</string>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java
index c96644c..03d52d0 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java
@@ -28,6 +28,7 @@
import android.os.Bundle;
import android.util.Log;
import android.view.View;
+
import androidx.annotation.Nullable;
/**
@@ -84,22 +85,28 @@
int statusCode = intent.getIntExtra(PackageInstaller.EXTRA_STATUS,
PackageInstaller.STATUS_FAILURE);
boolean returnResult = intent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false);
-
- if (returnResult) {
- int legacyStatus = intent.getIntExtra(PackageInstaller.EXTRA_LEGACY_STATUS,
+ int legacyStatus = intent.getIntExtra(PackageInstaller.EXTRA_LEGACY_STATUS,
PackageManager.INSTALL_FAILED_INTERNAL_ERROR);
+ // TODO (b/346655018): Use INSTALL_FAILED_ABORTED legacyCode in the condition
+ // statusCode can be STATUS_FAILURE_ABORTED if:
+ // 1. GPP blocks an install.
+ // 2. User denies ownership update explicitly.
+ // InstallFailed dialog must not be shown only when the user denies ownership update. We
+ // must show this dialog for all other install failures.
+ boolean userDenied = statusCode == PackageInstaller.STATUS_FAILURE_ABORTED
+ && legacyStatus != PackageManager.INSTALL_FAILED_VERIFICATION_TIMEOUT
+ && legacyStatus != PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
+
+ if (returnResult) {
// Return result if requested
Intent result = new Intent();
result.putExtra(Intent.EXTRA_INSTALL_RESULT, legacyStatus);
setResult(Activity.RESULT_FIRST_USER, result);
finish();
- } else if (statusCode != PackageInstaller.STATUS_FAILURE_ABORTED) {
- // statusCode will be STATUS_FAILURE_ABORTED if the update-owner confirmation dialog was
- // dismissed by the user. We don't want to show a InstallFailed dialog in this case.
- // If the user denies install permission for normal installs, this dialog will never be
- // triggered as the status code is returned from PackageInstallerActivity.java
-
+ } else if (userDenied) {
+ finish();
+ } else {
// Set header icon and title
PackageUtil.AppSnippet as = intent.getParcelableExtra(
PackageInstallerActivity.EXTRA_APP_SNIPPET, PackageUtil.AppSnippet.class);
@@ -127,8 +134,6 @@
// Get status messages
setExplanationFromErrorCode(statusCode);
- } else {
- finish();
}
}
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/MDnsUtils.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/MDnsUtils.java
old mode 100755
new mode 100644
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/ServiceResolver.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/ServiceResolver.java
old mode 100755
new mode 100644
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/VendorInfo.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/VendorInfo.java
old mode 100755
new mode 100644
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/XeroxPrintServiceRecommendationPlugin.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/XeroxPrintServiceRecommendationPlugin.java
old mode 100755
new mode 100644
diff --git a/packages/PrintSpooler/res/values-as/strings.xml b/packages/PrintSpooler/res/values-as/strings.xml
index 020eac7..950c5c8 100644
--- a/packages/PrintSpooler/res/values-as/strings.xml
+++ b/packages/PrintSpooler/res/values-as/strings.xml
@@ -100,7 +100,7 @@
</string-array>
<string-array name="orientation_labels">
<item msgid="4061931020926489228">"প\'ৰ্ট্ৰেইট"</item>
- <item msgid="3199660090246166812">"লেণ্ডস্কেইপ"</item>
+ <item msgid="3199660090246166812">"লেণ্ডস্কে’প"</item>
</string-array>
<string name="print_write_error_message" msgid="5787642615179572543">"ফাইলত লিখিব পৰা নহ\'ল"</string>
<string name="print_error_default_message" msgid="8602678405502922346">"দুঃখিত, প্ৰিণ্টিঙৰ কাম নহ\'ল। পুনৰ চেষ্টা কৰক।"</string>
diff --git a/packages/SettingsLib/ProfileSelector/res/values-pt-rBR/strings.xml b/packages/SettingsLib/ProfileSelector/res/values-pt-rBR/strings.xml
index eb33d57..75e1a18 100644
--- a/packages/SettingsLib/ProfileSelector/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/ProfileSelector/res/values-pt-rBR/strings.xml
@@ -19,5 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_category_personal" msgid="1142302328104700620">"Pessoal"</string>
<string name="settingslib_category_work" msgid="4867750733682444676">"Trabalho"</string>
- <string name="settingslib_category_private" msgid="5039276873477591386">"Particular"</string>
+ <string name="settingslib_category_private" msgid="5039276873477591386">"Privado"</string>
</resources>
diff --git a/packages/SettingsLib/ProfileSelector/res/values-pt/strings.xml b/packages/SettingsLib/ProfileSelector/res/values-pt/strings.xml
index eb33d57..75e1a18 100644
--- a/packages/SettingsLib/ProfileSelector/res/values-pt/strings.xml
+++ b/packages/SettingsLib/ProfileSelector/res/values-pt/strings.xml
@@ -19,5 +19,5 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="settingslib_category_personal" msgid="1142302328104700620">"Pessoal"</string>
<string name="settingslib_category_work" msgid="4867750733682444676">"Trabalho"</string>
- <string name="settingslib_category_private" msgid="5039276873477591386">"Particular"</string>
+ <string name="settingslib_category_private" msgid="5039276873477591386">"Privado"</string>
</resources>
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index 45667f5..232fa92 100644
--- a/packages/SettingsLib/Spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/build.gradle.kts
@@ -29,7 +29,7 @@
allprojects {
extra["androidTop"] = androidTop
- extra["jetpackComposeVersion"] = "1.7.0-alpha08"
+ extra["jetpackComposeVersion"] = "1.7.0-SNAPSHOT"
}
subprojects {
diff --git a/packages/SettingsLib/Spa/settings.gradle.kts b/packages/SettingsLib/Spa/settings.gradle.kts
index e003c81..31f462e 100644
--- a/packages/SettingsLib/Spa/settings.gradle.kts
+++ b/packages/SettingsLib/Spa/settings.gradle.kts
@@ -36,6 +36,9 @@
}
mavenCentral()
maven {
+ url = uri("https://androidx.dev/snapshots/builds/11846308/artifacts/repository")
+ }
+ maven {
url = uri("https://jitpack.io")
content {
includeGroup("com.github.PhilJay")
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index 4aa57b3..f98695e 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -57,13 +57,13 @@
api("androidx.slice:slice-builders:1.1.0-alpha02")
api("androidx.slice:slice-core:1.1.0-alpha02")
api("androidx.slice:slice-view:1.1.0-alpha02")
- api("androidx.compose.material3:material3:1.3.0-alpha06")
+ api("androidx.compose.material3:material3:1.3.0-SNAPSHOT")
api("androidx.compose.material:material-icons-extended:$jetpackComposeVersion")
api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion")
api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion")
api("androidx.lifecycle:lifecycle-livedata-ktx")
api("androidx.lifecycle:lifecycle-runtime-compose")
- api("androidx.navigation:navigation-compose:2.8.0-alpha08")
+ api("androidx.navigation:navigation-compose:2.8.0-beta01")
api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha")
api("com.google.android.material:material:1.11.0")
debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion")
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/AnnotatedStringResource.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/AnnotatedStringResource.kt
index 1a10bf0..2cf0d3b 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/AnnotatedStringResource.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/AnnotatedStringResource.kt
@@ -29,6 +29,7 @@
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.LinkAnnotation
import androidx.compose.ui.text.SpanStyle
+import androidx.compose.ui.text.TextLinkStyles
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
@@ -98,7 +99,9 @@
) {
val url = LinkAnnotation.Url(
url = urlSpan.url,
- style = SpanStyle(color = urlSpanColor, textDecoration = TextDecoration.Underline),
+ styles = TextLinkStyles(
+ style = SpanStyle(color = urlSpanColor, textDecoration = TextDecoration.Underline),
+ ),
)
addLink(url, start, end)
}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/AnnotatedStringResourceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/AnnotatedStringResourceTest.kt
index 612f9e5..4c1efd7 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/AnnotatedStringResourceTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/AnnotatedStringResourceTest.kt
@@ -21,6 +21,7 @@
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.LinkAnnotation
import androidx.compose.ui.text.SpanStyle
+import androidx.compose.ui.text.TextLinkStyles
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextDecoration
@@ -47,9 +48,11 @@
AnnotatedString.Range(
item = LinkAnnotation.Url(
url = "https://www.android.com/",
- style = SpanStyle(
- color = MaterialTheme.colorScheme.primary,
- textDecoration = TextDecoration.Underline,
+ styles = TextLinkStyles(
+ style = SpanStyle(
+ color = MaterialTheme.colorScheme.primary,
+ textDecoration = TextDecoration.Underline,
+ ),
),
),
start = 31,
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index f0fb4df..caeee06 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -710,7 +710,7 @@
<string name="keyboard_layout_default_label" msgid="1997292217218546957">"Standard"</string>
<string name="turn_screen_on_title" msgid="2662312432042116026">"Steuerelement zum Aktivieren des Displays"</string>
<string name="allow_turn_screen_on" msgid="6194845766392742639">"Aktivieren des Displays erlauben"</string>
- <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Einer App erlauben, das Display zu aktivieren. Wenn du diese Erlaubnis erteilst, kann die App jederzeit das Display aktivieren – auch ohne deine explizite Absicht."</string>
+ <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Einer App erlauben, das Display zu aktivieren. Wenn du diese Erlaubnis erteilst, kann die App jederzeit das Display aktivieren – auch ohne dass du dies beabsichtigst."</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"<xliff:g id="APP_NAME">%1$s</xliff:g> nicht mehr streamen?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="268234802198852753">"Wenn du <xliff:g id="SWITCHAPP">%1$s</xliff:g> streamst oder die Ausgabe änderst, wird dein aktueller Stream beendet"</string>
<string name="bt_le_audio_broadcast_dialog_switch_app" msgid="5749813313369517812">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> streamen"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 1381903..a25e179 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -632,7 +632,7 @@
<string name="profile_info_settings_title" msgid="105699672534365099">"ಪ್ರೊಫೈಲ್ ಮಾಹಿತಿ"</string>
<string name="user_need_lock_message" msgid="4311424336209509301">"ನೀವು ನಿರ್ಬಂಧಿತ ಪ್ರೊಫೈಲ್ ಅನ್ನು ರಚಿಸಬಹುದಾದರ ಮೊದಲು, ನಿಮ್ಮ ಅಪ್ಲಿಕೇಶನ್ಗಳು ಮತ್ತು ವೈಯಕ್ತಿಕ ಡೇಟಾವನ್ನು ರಕ್ಷಿಸಲು ನೀವು ಪರದೆಯ ಲಾಕ್ ಹೊಂದಿಸುವ ಅಗತ್ಯವಿದೆ."</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"ಲಾಕ್ ಹೊಂದಿಸಿ"</string>
- <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> ಗೆ ಬದಲಿಸಿ"</string>
+ <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>ಗೆ ಬದಲಿಸಿ"</string>
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"ಹೊಸ ಬಳಕೆದಾರರನ್ನು ರಚಿಸಲಾಗುತ್ತಿದೆ…"</string>
<string name="creating_new_guest_dialog_message" msgid="1114905602181350690">"ಹೊಸ ಅತಿಥಿಯನ್ನು ರಚಿಸಲಾಗುತ್ತಿದೆ…"</string>
<string name="add_user_failed" msgid="4809887794313944872">"ಹೊಸ ಬಳಕೆದಾರರನ್ನು ರಚಿಸಲು ವಿಫಲವಾಗಿದೆ"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 5032e35..1e33f88 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -641,7 +641,7 @@
<string name="edit_user_info_message" msgid="6677556031419002895">"यो डिभाइस प्रयोग गर्ने सबै जना तपाईंले छनौट गर्ने नाम र फोटो देख्न सक्ने छन्।"</string>
<string name="user_add_user" msgid="7876449291500212468">"प्रयोगकर्ता कनेक्ट गर्नुहोस्"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"अतिथि कनेक्ट गर्नुहोस्"</string>
- <string name="guest_exit_guest" msgid="5908239569510734136">"गेस्ट मोडबाट बाहिर निस्कियोस्"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"अथिति हटाउनुहोस्"</string>
<string name="guest_reset_guest" msgid="6110013010356013758">"अतिथि सत्र रिसेट गर्नुहोस्"</string>
<string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"अतिथिका रूपमा ब्राउज गर्ने सेसन रिसेट गर्ने हो?"</string>
<string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"यी अतिथि हटाउने हो?"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index d47a1f4..337b011 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -237,7 +237,7 @@
<string name="choose_profile" msgid="343803890897657450">"Escolher perfil"</string>
<string name="category_personal" msgid="6236798763159385225">"Pessoal"</string>
<string name="category_work" msgid="4014193632325996115">"Trabalho"</string>
- <string name="category_private" msgid="4244892185452788977">"Particular"</string>
+ <string name="category_private" msgid="4244892185452788977">"Privado"</string>
<string name="category_clone" msgid="1554511758987195974">"Clone"</string>
<string name="development_settings_title" msgid="140296922921597393">"Opções do desenvolvedor"</string>
<string name="development_settings_enable" msgid="4285094651288242183">"Ativar opções do desenvolvedor"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index d47a1f4..337b011 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -237,7 +237,7 @@
<string name="choose_profile" msgid="343803890897657450">"Escolher perfil"</string>
<string name="category_personal" msgid="6236798763159385225">"Pessoal"</string>
<string name="category_work" msgid="4014193632325996115">"Trabalho"</string>
- <string name="category_private" msgid="4244892185452788977">"Particular"</string>
+ <string name="category_private" msgid="4244892185452788977">"Privado"</string>
<string name="category_clone" msgid="1554511758987195974">"Clone"</string>
<string name="development_settings_title" msgid="140296922921597393">"Opções do desenvolvedor"</string>
<string name="development_settings_enable" msgid="4285094651288242183">"Ativar opções do desenvolvedor"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index ff62ba0..e8acd6e 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -161,7 +161,7 @@
<string name="bluetooth_pairing_decline" msgid="6483118841204885890">"ரத்துசெய்"</string>
<string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"இணைத்தலானது உங்கள் தொடர்புகள், அழைப்பு வரலாறுக்கான அணுகலை வழங்குகிறது."</string>
<string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> உடன் இணைய முடியவில்லை."</string>
- <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"தவறான பின் அல்லது கடவுச்சொல் காரணமாக <xliff:g id="DEVICE_NAME">%1$s</xliff:g> உடன் இணைக்க முடியவில்லை."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"தவறான பின் அல்லது கடவுச்சாவி காரணமாக <xliff:g id="DEVICE_NAME">%1$s</xliff:g> உடன் இணைக்க முடியவில்லை."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="2554424863101358857">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> உடன் இணைக்க முடியவில்லை."</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="5943444352777314442">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> இணைப்பதை நிராகரித்தது."</string>
<string name="bluetooth_talkback_computer" msgid="3736623135703893773">"கணினி"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 6aea659..e8bac31 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -492,9 +492,9 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - Tamamen şarj olmasına <xliff:g id="TIME">%2$s</xliff:g> kaldı"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Şarj işlemi optimize edildi"</string>
<string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Şarj ediliyor"</string>
- <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g> içinde tamamen dolacak"</string>
- <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> içinde tamamen şarj olacak"</string>
- <string name="power_remaining_charging_duration_only_v2" msgid="5358176435722950193">"<xliff:g id="TIME">%1$s</xliff:g> içinde tamamen şarj olacak"</string>
+ <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Tamamen dolacağı zaman: <xliff:g id="TIME">%3$s</xliff:g>"</string>
+ <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Tamamen şarj olacağı zaman: <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_remaining_charging_duration_only_v2" msgid="5358176435722950193">"Tamamen şarj olacağı zaman: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_fast_charging_duration_only_v2" msgid="6270950195810579563">"<xliff:g id="TIME">%1$s</xliff:g> itibarıyla tamamen dolacak"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Bilinmiyor"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Şarj oluyor"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
index ed964a9..b3e48b2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
@@ -209,44 +209,34 @@
CachedBluetoothDevice mainDevice = findMainDevice(cachedDevice);
if (mainDevice != null) {
if (mainDevice.isConnected()) {
- // When main device exists and in connected state, receiving sub device
- // connection. To refresh main device UI
+ // Sub/member device is connected and main device is connected
+ // To refresh main device UI
mainDevice.refresh();
} else {
- // When both Hearing Aid devices are disconnected, receiving sub device
- // connection. To switch content and dispatch to notify UI change
- mBtManager.getEventManager().dispatchDeviceRemoved(mainDevice);
- mainDevice.switchSubDeviceContent();
- mainDevice.refresh();
- // It is necessary to do remove and add for updating the mapping on
- // preference and device
- mBtManager.getEventManager().dispatchDeviceAdded(mainDevice);
+ // Sub/member device is connected and main device is disconnected
+ // To switch content and dispatch to notify UI change
+ switchDeviceContent(mainDevice, cachedDevice);
}
return true;
}
break;
case BluetoothProfile.STATE_DISCONNECTED:
- mainDevice = findMainDevice(cachedDevice);
if (cachedDevice.getUnpairing()) {
return true;
}
+ mainDevice = findMainDevice(cachedDevice);
if (mainDevice != null) {
- // When main device exists, receiving sub device disconnection
+ // Sub/member device is disconnected and main device exists
// To update main device UI
mainDevice.refresh();
return true;
}
- CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
- if (subDevice != null && subDevice.isConnected()) {
- // Main device is disconnected and sub device is connected
- // To copy data from sub device to main device
- mBtManager.getEventManager().dispatchDeviceRemoved(cachedDevice);
- cachedDevice.switchSubDeviceContent();
- cachedDevice.refresh();
- // It is necessary to do remove and add for updating the mapping on
- // preference and device
- mBtManager.getEventManager().dispatchDeviceAdded(cachedDevice);
-
+ CachedBluetoothDevice connectedSecondaryDevice = getConnectedSecondaryDevice(
+ cachedDevice);
+ if (connectedSecondaryDevice != null) {
+ // Main device is disconnected and sub/member device is connected
+ // To switch content and dispatch to notify UI change
+ switchDeviceContent(cachedDevice, connectedSecondaryDevice);
return true;
}
break;
@@ -254,6 +244,29 @@
return false;
}
+ private void switchDeviceContent(CachedBluetoothDevice mainDevice,
+ CachedBluetoothDevice secondaryDevice) {
+ mBtManager.getEventManager().dispatchDeviceRemoved(mainDevice);
+ if (mainDevice.getSubDevice() != null
+ && mainDevice.getSubDevice().equals(secondaryDevice)) {
+ mainDevice.switchSubDeviceContent();
+ } else {
+ mainDevice.switchMemberDeviceContent(secondaryDevice);
+ }
+ mainDevice.refresh();
+ // It is necessary to do remove and add for updating the mapping on
+ // preference and device
+ mBtManager.getEventManager().dispatchDeviceAdded(mainDevice);
+ }
+
+ private CachedBluetoothDevice getConnectedSecondaryDevice(CachedBluetoothDevice cachedDevice) {
+ if (cachedDevice.getSubDevice() != null && cachedDevice.getSubDevice().isConnected()) {
+ return cachedDevice.getSubDevice();
+ }
+ return cachedDevice.getMemberDevice().stream().filter(
+ CachedBluetoothDevice::isConnected).findAny().orElse(null);
+ }
+
void onActiveDeviceChanged(CachedBluetoothDevice device) {
if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_AUDIO_ROUTING)) {
if (device.isActiveDevice(BluetoothProfile.HEARING_AID) || device.isActiveDevice(
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/DeviceIconUtil.java b/packages/SettingsLib/src/com/android/settingslib/media/DeviceIconUtil.java
index 3de4933..717a8ee 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/DeviceIconUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/DeviceIconUtil.java
@@ -16,135 +16,136 @@
package com.android.settingslib.media;
+import static android.media.AudioDeviceInfo.AudioDeviceType;
+import static android.media.AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
+import static android.media.AudioDeviceInfo.TYPE_DOCK;
+import static android.media.AudioDeviceInfo.TYPE_HDMI;
+import static android.media.AudioDeviceInfo.TYPE_HDMI_ARC;
+import static android.media.AudioDeviceInfo.TYPE_HDMI_EARC;
+import static android.media.AudioDeviceInfo.TYPE_USB_ACCESSORY;
+import static android.media.AudioDeviceInfo.TYPE_USB_DEVICE;
+import static android.media.AudioDeviceInfo.TYPE_USB_HEADSET;
+import static android.media.AudioDeviceInfo.TYPE_WIRED_HEADPHONES;
+import static android.media.AudioDeviceInfo.TYPE_WIRED_HEADSET;
+
import android.annotation.DrawableRes;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
-import android.media.AudioDeviceInfo;
import android.media.MediaRoute2Info;
+import android.os.SystemProperties;
+import android.util.SparseIntArray;
+import androidx.annotation.NonNull;
+
+import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.R;
import com.android.settingslib.media.flags.Flags;
import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.Objects;
/** A util class to get the appropriate icon for different device types. */
public class DeviceIconUtil {
- // A default icon to use if the type is not present in the map.
- @DrawableRes private static final int DEFAULT_ICON = R.drawable.ic_smartphone;
- @DrawableRes private static final int DEFAULT_ICON_TV = R.drawable.ic_media_speaker_device;
-
- // A map from a @AudioDeviceInfo.AudioDeviceType to full device information.
- private final Map<Integer, Device> mAudioDeviceTypeToIconMap = new HashMap<>();
- // A map from a @MediaRoute2Info.Type to full device information.
- private final Map<Integer, Device> mMediaRouteTypeToIconMap = new HashMap<>();
+ private static final SparseIntArray AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE = new SparseIntArray();
private final boolean mIsTv;
-
- public DeviceIconUtil(Context context) {
- this(context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK));
+ private final boolean mIsTablet;
+ private final Context mContext;
+ public DeviceIconUtil(@NonNull Context context) {
+ mContext = Objects.requireNonNull(context);
+ mIsTv =
+ mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)
+ && Flags.enableTvMediaOutputDialog();
+ mIsTablet =
+ Arrays.asList(SystemProperties.get("ro.build.characteristics").split(","))
+ .contains("tablet");
}
- public DeviceIconUtil(boolean isTv) {
- mIsTv = isTv && Flags.enableTvMediaOutputDialog();
- List<Device> deviceList = Arrays.asList(
- new Device(
- AudioDeviceInfo.TYPE_USB_DEVICE,
- MediaRoute2Info.TYPE_USB_DEVICE,
- R.drawable.ic_headphone),
- new Device(
- AudioDeviceInfo.TYPE_USB_HEADSET,
- MediaRoute2Info.TYPE_USB_HEADSET,
- R.drawable.ic_headphone),
- new Device(
- AudioDeviceInfo.TYPE_USB_ACCESSORY,
- MediaRoute2Info.TYPE_USB_ACCESSORY,
- mIsTv ? R.drawable.ic_usb : R.drawable.ic_headphone),
- new Device(
- AudioDeviceInfo.TYPE_DOCK,
- MediaRoute2Info.TYPE_DOCK,
- R.drawable.ic_dock_device),
- new Device(
- AudioDeviceInfo.TYPE_HDMI,
- MediaRoute2Info.TYPE_HDMI,
- mIsTv ? R.drawable.ic_tv : R.drawable.ic_external_display),
- new Device(
- AudioDeviceInfo.TYPE_HDMI_ARC,
- MediaRoute2Info.TYPE_HDMI_ARC,
- mIsTv ? R.drawable.ic_hdmi : R.drawable.ic_external_display),
- new Device(
- AudioDeviceInfo.TYPE_HDMI_EARC,
- MediaRoute2Info.TYPE_HDMI_EARC,
- mIsTv ? R.drawable.ic_hdmi : R.drawable.ic_external_display),
- new Device(
- AudioDeviceInfo.TYPE_WIRED_HEADSET,
- MediaRoute2Info.TYPE_WIRED_HEADSET,
- mIsTv ? R.drawable.ic_wired_device : R.drawable.ic_headphone),
- new Device(
- AudioDeviceInfo.TYPE_WIRED_HEADPHONES,
- MediaRoute2Info.TYPE_WIRED_HEADPHONES,
- mIsTv ? R.drawable.ic_wired_device : R.drawable.ic_headphone),
- new Device(
- AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
- MediaRoute2Info.TYPE_BUILTIN_SPEAKER,
- mIsTv ? R.drawable.ic_tv : R.drawable.ic_smartphone));
- for (int i = 0; i < deviceList.size(); i++) {
- Device device = deviceList.get(i);
- mAudioDeviceTypeToIconMap.put(device.mAudioDeviceType, device);
- mMediaRouteTypeToIconMap.put(device.mMediaRouteType, device);
- }
- }
-
- private int getDefaultIcon() {
- return mIsTv ? DEFAULT_ICON_TV : DEFAULT_ICON;
+ @VisibleForTesting
+ /* package */ DeviceIconUtil(boolean isTv) {
+ mContext = null;
+ mIsTv = isTv;
+ mIsTablet = false;
}
/** Returns a drawable for an icon representing the given audioDeviceType. */
- public Drawable getIconFromAudioDeviceType(
- @AudioDeviceInfo.AudioDeviceType int audioDeviceType, Context context) {
- return context.getDrawable(getIconResIdFromAudioDeviceType(audioDeviceType));
+ public Drawable getIconFromAudioDeviceType(@AudioDeviceType int audioDeviceType) {
+ return mContext.getDrawable(getIconResIdFromAudioDeviceType(audioDeviceType));
}
/** Returns a drawable res ID for an icon representing the given audioDeviceType. */
@DrawableRes
- public int getIconResIdFromAudioDeviceType(
- @AudioDeviceInfo.AudioDeviceType int audioDeviceType) {
- if (mAudioDeviceTypeToIconMap.containsKey(audioDeviceType)) {
- return mAudioDeviceTypeToIconMap.get(audioDeviceType).mIconDrawableRes;
- }
- return getDefaultIcon();
+ public int getIconResIdFromAudioDeviceType(@AudioDeviceType int audioDeviceType) {
+ int mediaRouteType =
+ AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.get(audioDeviceType, /* defaultValue */ -1);
+ return getIconResIdFromMediaRouteType(mediaRouteType);
}
/** Returns a drawable res ID for an icon representing the given mediaRouteType. */
@DrawableRes
- public int getIconResIdFromMediaRouteType(
- @MediaRoute2Info.Type int mediaRouteType) {
- if (mMediaRouteTypeToIconMap.containsKey(mediaRouteType)) {
- return mMediaRouteTypeToIconMap.get(mediaRouteType).mIconDrawableRes;
- }
- return getDefaultIcon();
+ public int getIconResIdFromMediaRouteType(@MediaRoute2Info.Type int type) {
+ return mIsTv
+ ? getIconResourceIdForTv(type)
+ : getIconResourceIdForPhoneOrTablet(type, mIsTablet);
}
- private static class Device {
- @AudioDeviceInfo.AudioDeviceType
- private final int mAudioDeviceType;
+ @SuppressLint("SwitchIntDef")
+ @DrawableRes
+ private static int getIconResourceIdForPhoneOrTablet(
+ @MediaRoute2Info.Type int type, boolean isTablet) {
+ int defaultResId = isTablet ? R.drawable.ic_media_tablet : R.drawable.ic_smartphone;
- @MediaRoute2Info.Type
- private final int mMediaRouteType;
+ return switch (type) {
+ case MediaRoute2Info.TYPE_USB_DEVICE,
+ MediaRoute2Info.TYPE_USB_HEADSET,
+ MediaRoute2Info.TYPE_USB_ACCESSORY,
+ MediaRoute2Info.TYPE_WIRED_HEADSET,
+ MediaRoute2Info.TYPE_WIRED_HEADPHONES ->
+ R.drawable.ic_headphone;
+ case MediaRoute2Info.TYPE_DOCK -> R.drawable.ic_dock_device;
+ case MediaRoute2Info.TYPE_HDMI,
+ MediaRoute2Info.TYPE_HDMI_ARC,
+ MediaRoute2Info.TYPE_HDMI_EARC ->
+ R.drawable.ic_external_display;
+ default -> defaultResId; // Includes TYPE_BUILTIN_SPEAKER.
+ };
+ }
- @DrawableRes
- private final int mIconDrawableRes;
+ @SuppressLint("SwitchIntDef")
+ @DrawableRes
+ private static int getIconResourceIdForTv(@MediaRoute2Info.Type int type) {
+ return switch (type) {
+ case MediaRoute2Info.TYPE_USB_DEVICE, MediaRoute2Info.TYPE_USB_HEADSET ->
+ R.drawable.ic_headphone;
+ case MediaRoute2Info.TYPE_USB_ACCESSORY -> R.drawable.ic_usb;
+ case MediaRoute2Info.TYPE_DOCK -> R.drawable.ic_dock_device;
+ case MediaRoute2Info.TYPE_HDMI, MediaRoute2Info.TYPE_BUILTIN_SPEAKER ->
+ R.drawable.ic_tv;
+ case MediaRoute2Info.TYPE_HDMI_ARC, MediaRoute2Info.TYPE_HDMI_EARC ->
+ R.drawable.ic_hdmi;
+ case MediaRoute2Info.TYPE_WIRED_HEADSET, MediaRoute2Info.TYPE_WIRED_HEADPHONES ->
+ R.drawable.ic_wired_device;
+ default -> R.drawable.ic_media_speaker_device;
+ };
+ }
- Device(@AudioDeviceInfo.AudioDeviceType int audioDeviceType,
- @MediaRoute2Info.Type int mediaRouteType,
- @DrawableRes int iconDrawableRes) {
- mAudioDeviceType = audioDeviceType;
- mMediaRouteType = mediaRouteType;
- mIconDrawableRes = iconDrawableRes;
- }
+ static {
+ AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_USB_DEVICE, MediaRoute2Info.TYPE_USB_DEVICE);
+ AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_USB_HEADSET, MediaRoute2Info.TYPE_USB_HEADSET);
+ AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(
+ TYPE_USB_ACCESSORY, MediaRoute2Info.TYPE_USB_ACCESSORY);
+ AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_DOCK, MediaRoute2Info.TYPE_DOCK);
+ AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_HDMI, MediaRoute2Info.TYPE_HDMI);
+ AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_HDMI_ARC, MediaRoute2Info.TYPE_HDMI_ARC);
+ AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_HDMI_EARC, MediaRoute2Info.TYPE_HDMI_EARC);
+ AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(
+ TYPE_WIRED_HEADSET, MediaRoute2Info.TYPE_WIRED_HEADSET);
+ AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(
+ TYPE_WIRED_HEADPHONES, MediaRoute2Info.TYPE_WIRED_HEADPHONES);
+ AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(
+ TYPE_BUILTIN_SPEAKER, MediaRoute2Info.TYPE_BUILTIN_SPEAKER);
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
index 4188d2e..bf927a1 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
@@ -681,6 +681,53 @@
verify(mCachedDevice1).refresh();
}
+
+ /**
+ * Test onProfileConnectionStateChangedIfProcessed.
+ * When main device is disconnected, to verify switch() result for member device connected
+ * event
+ */
+ @Test
+ public void onProfileConnectionStateChanged_connect_member_mainDisconnected_switch() {
+ when(mCachedDevice1.isConnected()).thenReturn(false);
+ when(mCachedDevice1.getGroupId()).thenReturn(GROUP_ID_1);
+ when(mCachedDevice2.getGroupId()).thenReturn(GROUP_ID_1);
+ mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
+ mCachedDevice1.addMemberDevice(mCachedDevice2);
+
+ assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice1);
+ assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice2);
+ assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(
+ mCachedDevice2, BluetoothProfile.STATE_CONNECTED)).isTrue();
+
+ assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice2);
+ assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice1);
+ verify(mCachedDevice1).refresh();
+ }
+
+ /**
+ * Test onProfileConnectionStateChangedIfProcessed.
+ * When member device is connected, to verify switch() result for main device disconnected
+ * event
+ */
+ @Test
+ public void onProfileConnectionStateChanged_disconnect_main_subDeviceConnected_switch() {
+ when(mCachedDevice2.isConnected()).thenReturn(true);
+ when(mCachedDevice1.getGroupId()).thenReturn(GROUP_ID_1);
+ when(mCachedDevice2.getGroupId()).thenReturn(GROUP_ID_1);
+ mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
+ mCachedDevice1.addMemberDevice(mCachedDevice2);
+
+ assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice1);
+ assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice2);
+ assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(
+ mCachedDevice1, BluetoothProfile.STATE_DISCONNECTED)).isTrue();
+
+ assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice2);
+ assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice1);
+ verify(mCachedDevice1).refresh();
+ }
+
@Test
public void onActiveDeviceChanged_connected_callSetStrategies() {
when(mHelper.getMatchedHearingDeviceAttributes(mCachedDevice1)).thenReturn(
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java
index 8edda1a..883640d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java
@@ -18,6 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
+import android.content.Context;
import android.media.AudioDeviceInfo;
import android.media.MediaRoute2Info;
import android.platform.test.flag.junit.SetFlagsRule;
@@ -30,6 +31,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadows.ShadowSystemProperties;
@RunWith(RobolectricTestRunner.class)
public class DeviceIconUtilTest {
@@ -37,9 +40,12 @@
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ private Context mContext;
+
@Before
public void setup() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TV_MEDIA_OUTPUT_DIALOG);
+ mContext = RuntimeEnvironment.getApplication();
}
@Test
@@ -171,6 +177,14 @@
}
@Test
+ public void getIconResIdFromMediaRouteType_onTablet_builtinSpeaker_isTablet() {
+ ShadowSystemProperties.override("ro.build.characteristics", "tablet");
+ assertThat(new DeviceIconUtil(mContext)
+ .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_BUILTIN_SPEAKER))
+ .isEqualTo(R.drawable.ic_media_tablet);
+ }
+
+ @Test
public void getIconResIdFromMediaRouteType_unsupportedType_isSmartphone() {
assertThat(new DeviceIconUtil(/* isTv */ false)
.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_UNKNOWN))
@@ -178,6 +192,14 @@
}
@Test
+ public void getIconResIdFromMediaRouteType_onTablet_unsupportedType_isTablet() {
+ ShadowSystemProperties.override("ro.build.characteristics", "tablet");
+ assertThat(new DeviceIconUtil(mContext)
+ .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_UNKNOWN))
+ .isEqualTo(R.drawable.ic_media_tablet);
+ }
+
+ @Test
public void getIconResIdFromMediaRouteType_tv_unsupportedType_isSpeaker() {
assertThat(new DeviceIconUtil(/* isTv */ true)
.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_UNKNOWN))
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index 5245456..00fb7a1 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -80,6 +80,7 @@
Settings.System.SIP_RECEIVE_CALLS,
Settings.System.POINTER_SPEED,
Settings.System.POINTER_FILL_STYLE,
+ Settings.System.POINTER_SCALE,
Settings.System.VIBRATE_ON,
Settings.System.VIBRATE_WHEN_RINGING,
Settings.System.RINGTONE,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 2c3be4c..4235bc4 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -26,6 +26,8 @@
import static android.provider.settings.validators.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.URI_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.VIBRATION_INTENSITY_VALIDATOR;
+import static android.view.PointerIcon.DEFAULT_POINTER_SCALE;
+import static android.view.PointerIcon.LARGE_POINTER_SCALE;
import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_BEGIN;
import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_END;
@@ -211,6 +213,8 @@
VALIDATORS.put(System.POINTER_FILL_STYLE,
new InclusiveIntegerRangeValidator(POINTER_ICON_VECTOR_STYLE_FILL_BEGIN,
POINTER_ICON_VECTOR_STYLE_FILL_END));
+ VALIDATORS.put(System.POINTER_SCALE,
+ new InclusiveFloatRangeValidator(DEFAULT_POINTER_SCALE, LARGE_POINTER_SCALE));
VALIDATORS.put(System.TOUCHPAD_POINTER_SPEED, new InclusiveIntegerRangeValidator(-7, 7));
VALIDATORS.put(System.TOUCHPAD_NATURAL_SCROLLING, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.TOUCHPAD_TAP_TO_CLICK, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/NonWritableNamespacesForBackgroundUserPrefixes.java b/packages/SettingsProvider/src/com/android/providers/settings/NonWritableNamespacesForBackgroundUserPrefixes.java
new file mode 100644
index 0000000..5c5ca46
--- /dev/null
+++ b/packages/SettingsProvider/src/com/android/providers/settings/NonWritableNamespacesForBackgroundUserPrefixes.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.settings;
+
+import android.util.ArraySet;
+
+import java.util.Arrays;
+import java.util.Set;
+
+/**
+ * Contains the list of prefixes for namespaces in which nothing can be written by background
+ * user.
+ *
+ * <p>
+ * The list in enforced is Auto devices only. To add to
+ * the list, create a change and tag the OWNER. In the change description, include a
+ * description of the flag's functionality, and a justification for why it needs to be
+ * denylisted.
+ */
+final class NonWritableNamespacesForBackgroundUserPrefixes {
+ public static final Set<String> DENYLIST =
+ new ArraySet<String>(Arrays.asList(
+ "game_overlay"
+ ));
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 625b8e4..384cb7e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -2915,6 +2915,9 @@
dumpSetting(s, p,
Settings.System.POINTER_FILL_STYLE,
SystemSettingsProto.Pointer.POINTER_FILL_STYLE);
+ dumpSetting(s, p,
+ Settings.System.POINTER_SCALE,
+ SystemSettingsProto.Pointer.POINTER_SCALE);
p.end(pointerToken);
dumpSetting(s, p,
Settings.System.POINTER_SPEED,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 15f8a7b..d54236e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2392,8 +2392,12 @@
== PackageManager.PERMISSION_GRANTED;
boolean isRoot = Binder.getCallingUid() == Process.ROOT_UID;
- if (isRoot || hasWritePermission) {
+ if (isRoot) {
return;
+ }
+
+ if (hasWritePermission) {
+ assertCallingUserDenyList(flags);
} else if (hasAllowlistPermission) {
for (String flag : flags) {
boolean namespaceAllowed = false;
@@ -2410,12 +2414,49 @@
+ "'; allowlist permission granted, but must add flag to the allowlist.");
}
}
+ assertCallingUserDenyList(flags);
} else {
throw new SecurityException("Permission denial to mutate flag, must have root, "
+ "WRITE_DEVICE_CONFIG, or WRITE_ALLOWLISTED_DEVICE_CONFIG");
}
}
+ // The check is added mainly for auto devices. On auto devices, it is possible that
+ // multiple users are visible simultaneously using visible background users.
+ // In such cases, it is desired that Non-current user (ex. visible background users) can
+ // only change settings for certain namespaces.
+ private void assertCallingUserDenyList(@NonNull Set<String> flags) {
+ if (!UserManager.isVisibleBackgroundUsersEnabled()) {
+ // enforce the deny list only on devices supporting visible background user.
+ return;
+ }
+
+ int callingUser = UserHandle.getCallingUserId();
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ int currentUser = ActivityManager.getCurrentUser();
+ if (callingUser == currentUser) {
+ // enforce the deny list only if the caller is not current user. Currently only auto
+ // uses background visible user, and auto doesn't support profiles so profiles of
+ // current users is not checked here.
+ return;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+
+ for (String flag : flags) {
+ for (String denylistedPrefix :
+ NonWritableNamespacesForBackgroundUserPrefixes.DENYLIST) {
+ if (flag.startsWith(denylistedPrefix)) {
+ throw new SecurityException("Permission denial for flag '" + flag
+ + "' for background user " + callingUser + ". Namespace is added to "
+ + "denylist.");
+ }
+ }
+ }
+ }
+
private static void warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk(
int targetSdkVersion, String name) {
// If the app targets Lollipop MR1 or older SDK we warn, otherwise crash.
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index c2c334b..58c39b4 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -78,10 +78,50 @@
visibility: ["//visibility:private"],
}
+// Tests where robolectric conversion caused errors in SystemUITests at runtime
+filegroup {
+ name: "SystemUI-tests-broken-robofiles-sysui-run",
+ srcs: [
+ "tests/src/**/systemui/broadcast/BroadcastDispatcherTest.kt",
+ "tests/src/**/systemui/broadcast/ActionReceiverTest.kt",
+ "tests/src/**/systemui/doze/DozeMachineTest.java",
+ "tests/src/**/systemui/globalactions/GlobalActionsDialogLiteTest.java",
+ "tests/src/**/systemui/globalactions/GlobalActionsImeTest.java",
+ "tests/src/**/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt",
+ "tests/src/**/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt",
+ "tests/src/**/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt",
+ "tests/src/**/systemui/media/dialog/MediaOutputAdapterTest.java",
+ "tests/src/**/systemui/media/dialog/MediaOutputBaseDialogTest.java",
+ "tests/src/**/systemui/media/dialog/MediaOutputBroadcastDialogTest.java",
+ "tests/src/**/systemui/media/dialog/MediaOutputDialogTest.java",
+ "tests/src/**/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt",
+ ],
+}
+
// Tests where robolectric failed at runtime. (go/multivalent-tests)
filegroup {
name: "SystemUI-tests-broken-robofiles-run",
srcs: [
+ "tests/src/**/systemui/accessibility/AccessibilityButtonModeObserverTest.java",
+ "tests/src/**/systemui/accessibility/AccessibilityButtonTargetsObserverTest.java",
+ "tests/src/**/systemui/accessibility/FullscreenMagnificationControllerTest.java",
+ "tests/src/**/systemui/accessibility/WindowMagnificationAnimationControllerTest.java",
+ "tests/src/**/systemui/animation/FontInterpolatorTest.kt",
+ "tests/src/**/systemui/animation/TextAnimatorTest.kt",
+ "tests/src/**/systemui/animation/TextInterpolatorTest.kt",
+ "tests/src/**/systemui/animation/ActivityTransitionAnimatorTest.kt",
+ "tests/src/**/systemui/animation/AnimatorTestRuleOrderTest.kt",
+ "tests/src/**/systemui/animation/DialogTransitionAnimatorTest.kt",
+ "tests/src/**/systemui/broadcast/ActionReceiverTest.kt",
+ "tests/src/**/systemui/broadcast/BroadcastDispatcherTest.kt",
+ "tests/src/**/systemui/compose/ComposeInitializerTest.kt",
+ "tests/src/**/systemui/controls/ui/ControlsActivityTest.kt",
+ "tests/src/**/systemui/controls/management/ControlsEditingActivityTest.kt",
+ "tests/src/**/systemui/controls/management/ControlsRequestDialogTest.kt",
+ "tests/src/**/systemui/controls/ui/DetailDialogTest.kt",
+ "tests/src/**/systemui/doze/DozeMachineTest.kt",
+ "tests/src/**/systemui/fontscaling/FontScalingDialogDelegateTest.kt",
+ "tests/src/**/systemui/keyguard/CustomizationProviderTest.kt",
"tests/src/**/systemui/globalactions/GlobalActionsColumnLayoutTest.java",
"tests/src/**/systemui/globalactions/GlobalActionsDialogLiteTest.java",
"tests/src/**/systemui/globalactions/GlobalActionsImeTest.java",
@@ -176,9 +216,7 @@
],
}
-// We are running robolectric tests in the tests directory as well as
-// multivalent tests. If you add a test, and it doesn't run in robolectric,
-// it should be added to this exclusion list. go/multivalent-tests
+// Tests where robolectric failed at compile time. (go/multivalent-tests)
filegroup {
name: "SystemUI-tests-broken-robofiles-compile",
srcs: [
@@ -330,6 +368,7 @@
"tests/src/**/systemui/shared/system/RemoteTransitionTest.java",
"tests/src/**/systemui/navigationbar/NavigationBarControllerImplTest.java",
"tests/src/**/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt",
+ "tests/src/**/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt",
"tests/src/**/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt",
"tests/src/**/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivityTest.kt",
"tests/src/**/systemui/notetask/shortcut/LaunchNoteTaskActivityTest.kt",
@@ -746,7 +785,6 @@
kotlincflags: ["-Xjvm-default=all"],
optimize: {
shrink_resources: false,
- optimized_shrink_resources: false,
proguard_flags_files: ["proguard.flags"],
},
@@ -811,6 +849,7 @@
exclude_srcs: [
":SystemUI-tests-broken-robofiles-compile",
":SystemUI-tests-broken-robofiles-run",
+ ":SystemUI-tests-broken-robofiles-sysui-run",
],
static_libs: [
"RoboTestLibraries",
@@ -882,7 +921,6 @@
optimize: true,
shrink: true,
shrink_resources: true,
- optimized_shrink_resources: true,
ignore_warnings: false,
proguard_compatibility: false,
},
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index a9c4399..8a99263 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -387,7 +387,7 @@
android:killAfterRestore="false"
android:hardwareAccelerated="true"
android:label="@string/app_label"
- android:icon="@drawable/android14_patch_adaptive"
+ android:icon="@drawable/android15_patch_adaptive"
android:process="com.android.systemui"
android:supportsRtl="true"
android:theme="@style/Theme.SystemUI"
@@ -650,6 +650,7 @@
<!-- started from MediaProjectionManager -->
<activity
android:name=".mediaprojection.permission.MediaProjectionPermissionActivity"
+ android:showForAllUsers="true"
android:exported="true"
android:theme="@style/Theme.SystemUI.MediaProjectionAlertDialog"
android:finishOnCloseSystemDialogs="true"
@@ -660,6 +661,7 @@
<activity
android:name=".mediaprojection.appselector.MediaProjectionAppSelectorActivity"
android:theme="@style/Theme.SystemUI.MediaProjectionAppSelector"
+ android:showForAllUsers="true"
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true"
android:documentLaunchMode="never"
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt-rBR/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt-rBR/strings.xml
index 160d310..f12278a 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt-rBR/strings.xml
@@ -2,7 +2,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="accessibility_menu_service_name" msgid="730136711554740131">"Menu de acessibilidade"</string>
- <string name="accessibility_menu_intro" msgid="3164193281544042394">"\"Acessibilidade\" é um menu grande mostrado na tela para controlar seu dispositivo. Você pode bloquear o dispositivo, controlar o volume e o brilho, fazer capturas de tela e muito mais."</string>
+ <string name="accessibility_menu_intro" msgid="3164193281544042394">"Com este menu de tamanho grande na tela, você consegue bloquear o dispositivo, ajustar o volume e o brilho, fazer capturas de tela, entre outras funções de controle do aparelho."</string>
<string name="assistant_label" msgid="6796392082252272356">"Assistente"</string>
<string name="assistant_utterance" msgid="65509599221141377">"Google Assistente"</string>
<string name="a11y_settings_label" msgid="3977714687248445050">"Configurações de acessibilidade"</string>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt/strings.xml
index 160d310..f12278a 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt/strings.xml
@@ -2,7 +2,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="accessibility_menu_service_name" msgid="730136711554740131">"Menu de acessibilidade"</string>
- <string name="accessibility_menu_intro" msgid="3164193281544042394">"\"Acessibilidade\" é um menu grande mostrado na tela para controlar seu dispositivo. Você pode bloquear o dispositivo, controlar o volume e o brilho, fazer capturas de tela e muito mais."</string>
+ <string name="accessibility_menu_intro" msgid="3164193281544042394">"Com este menu de tamanho grande na tela, você consegue bloquear o dispositivo, ajustar o volume e o brilho, fazer capturas de tela, entre outras funções de controle do aparelho."</string>
<string name="assistant_label" msgid="6796392082252272356">"Assistente"</string>
<string name="assistant_utterance" msgid="65509599221141377">"Google Assistente"</string>
<string name="a11y_settings_label" msgid="3977714687248445050">"Configurações de acessibilidade"</string>
diff --git a/packages/SystemUI/aconfig/communal.aconfig b/packages/SystemUI/aconfig/communal.aconfig
index 2e9af7e..afcd8a9 100644
--- a/packages/SystemUI/aconfig/communal.aconfig
+++ b/packages/SystemUI/aconfig/communal.aconfig
@@ -7,3 +7,13 @@
description: "Enables the communal hub experience"
bug: "304584416"
}
+
+flag {
+ name: "enable_widget_picker_size_filter"
+ namespace: "communal"
+ description: "Enables passing a size filter to the widget picker"
+ bug: "345482907"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 4311e79..1cbf67e 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -981,6 +981,16 @@
}
flag {
+ name: "media_controls_lockscreen_shade_bug_fix"
+ namespace: "systemui"
+ description: "Use ShadeInteractor for media location changes"
+ bug: "319244625"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
namespace: "systemui"
name: "enable_view_capture_tracing"
description: "Enables view capture tracing in System UI."
@@ -1038,6 +1048,7 @@
bug: "343505271"
}
+
flag {
name: "new_touchpad_gestures_tutorial"
namespace: "systemui"
@@ -1056,6 +1067,16 @@
}
flag {
+ name: "enable_efficient_display_repository"
+ namespace: "systemui"
+ description: "Decide whether to use the new implementation of DisplayRepository that minimizes binder calls and background lock contention."
+ bug: "345472038"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "notification_media_manager_background_execution"
namespace: "systemui"
description: "Decide whether to execute binder calls in background thread"
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index a1f8f1b..c4659cf 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -55,6 +55,7 @@
import com.android.systemui.communal.shared.model.CommunalBackgroundType
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalTransitionKeys
+import com.android.systemui.communal.ui.compose.Dimensions.SlideOffsetY
import com.android.systemui.communal.ui.compose.extensions.allowGestures
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.communal.util.CommunalColors
@@ -68,6 +69,7 @@
val Grid = ElementKey("CommunalContent")
val LockIcon = ElementKey("CommunalLockIcon")
val IndicationArea = ElementKey("CommunalIndicationArea")
+ val StatusBar = ElementKey("StatusBar")
}
}
@@ -75,6 +77,15 @@
override fun matches(key: ElementKey, scene: SceneKey) = true
}
+private object TransitionDuration {
+ const val BETWEEN_HUB_AND_EDIT_MODE_MS = 1000
+ const val EDIT_MODE_TO_HUB_CONTENT_MS = 167
+ const val EDIT_MODE_TO_HUB_GRID_DELAY_MS = 167
+ const val EDIT_MODE_TO_HUB_GRID_END_MS =
+ EDIT_MODE_TO_HUB_GRID_DELAY_MS + EDIT_MODE_TO_HUB_CONTENT_MS
+ const val HUB_TO_EDIT_MODE_CONTENT_MS = 250
+}
+
val sceneTransitions = transitions {
to(CommunalScenes.Communal, key = CommunalTransitionKeys.SimpleFade) {
spec = tween(durationMillis = 250)
@@ -92,9 +103,34 @@
fade(Communal.Elements.Grid)
fade(Communal.Elements.IndicationArea)
fade(Communal.Elements.LockIcon)
+ fade(Communal.Elements.StatusBar)
}
timestampRange(startMillis = 167, endMillis = 334) { fade(Communal.Elements.Scrim) }
}
+ to(CommunalScenes.Blank, key = CommunalTransitionKeys.ToEditMode) {
+ spec = tween(durationMillis = TransitionDuration.BETWEEN_HUB_AND_EDIT_MODE_MS)
+ timestampRange(endMillis = TransitionDuration.HUB_TO_EDIT_MODE_CONTENT_MS) {
+ fade(Communal.Elements.Grid)
+ fade(Communal.Elements.IndicationArea)
+ fade(Communal.Elements.LockIcon)
+ }
+ fade(Communal.Elements.Scrim)
+ }
+ to(CommunalScenes.Communal, key = CommunalTransitionKeys.FromEditMode) {
+ spec = tween(durationMillis = TransitionDuration.BETWEEN_HUB_AND_EDIT_MODE_MS)
+ translate(Communal.Elements.Grid, y = SlideOffsetY)
+ timestampRange(endMillis = TransitionDuration.EDIT_MODE_TO_HUB_CONTENT_MS) {
+ fade(Communal.Elements.IndicationArea)
+ fade(Communal.Elements.LockIcon)
+ fade(Communal.Elements.Scrim)
+ }
+ timestampRange(
+ startMillis = TransitionDuration.EDIT_MODE_TO_HUB_GRID_DELAY_MS,
+ endMillis = TransitionDuration.EDIT_MODE_TO_HUB_GRID_END_MS
+ ) {
+ fade(Communal.Elements.Grid)
+ }
+ }
}
/**
@@ -252,7 +288,10 @@
Box(
Modifier.matchParentSize()
.background(colors.primary)
- .animatedRadialGradientBackground(colors.primary, colors.primaryContainer)
+ .animatedRadialGradientBackground(
+ toColor = colors.primary,
+ fromColor = colors.primaryContainer.copy(alpha = 0.6f)
+ )
)
BackgroundTopScrim()
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
index 77665155..18085ab 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
@@ -16,13 +16,17 @@
package com.android.systemui.communal.ui.compose
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.unit.IntRect
import com.android.compose.animation.scene.SceneScope
import com.android.compose.theme.LocalAndroidColorScheme
+import com.android.systemui.communal.smartspace.SmartspaceInteractionHandler
+import com.android.systemui.communal.ui.compose.section.AmbientStatusBarSection
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.keyguard.ui.composable.blueprint.BlueprintAlignmentLines
import com.android.systemui.keyguard.ui.composable.section.LockSection
@@ -34,20 +38,27 @@
@Inject
constructor(
private val viewModel: CommunalViewModel,
+ private val interactionHandler: SmartspaceInteractionHandler,
private val dialogFactory: SystemUIDialogFactory,
private val lockSection: LockSection,
+ private val ambientStatusBarSection: AmbientStatusBarSection,
) {
-
@Composable
fun SceneScope.Content(modifier: Modifier = Modifier) {
Layout(
modifier = modifier.fillMaxSize(),
content = {
- CommunalHub(
- viewModel = viewModel,
- dialogFactory = dialogFactory,
- modifier = Modifier.element(Communal.Elements.Grid)
- )
+ Box(modifier = Modifier.fillMaxSize()) {
+ with(ambientStatusBarSection) {
+ AmbientStatusBar(modifier = Modifier.fillMaxWidth())
+ }
+ CommunalHub(
+ viewModel = viewModel,
+ interactionHandler = interactionHandler,
+ dialogFactory = dialogFactory,
+ modifier = Modifier.element(Communal.Elements.Grid)
+ )
+ }
with(lockSection) {
LockIcon(
overrideColor = LocalAndroidColorScheme.current.onPrimaryContainer,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 1f7f07b..927890e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -16,17 +16,16 @@
package com.android.systemui.communal.ui.compose
-import android.appwidget.AppWidgetHostView
import android.graphics.drawable.Icon
import android.os.Bundle
import android.util.SizeF
import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
import android.widget.FrameLayout
+import android.widget.RemoteViews
import androidx.annotation.VisibleForTesting
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.AnimatedVisibilityScope
-import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.animateFloatAsState
@@ -34,6 +33,8 @@
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
+import androidx.compose.animation.slideInVertically
+import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
@@ -46,6 +47,7 @@
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
@@ -128,6 +130,7 @@
import androidx.compose.ui.window.Popup
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.window.layout.WindowMetricsCalculator
+import com.android.compose.animation.Easings.Emphasized
import com.android.compose.modifiers.thenIf
import com.android.compose.theme.LocalAndroidColorScheme
import com.android.compose.ui.graphics.painter.rememberDrawablePainter
@@ -144,6 +147,7 @@
import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.communal.ui.viewmodel.PopupType
+import com.android.systemui.communal.widgets.SmartspaceAppWidgetHostView
import com.android.systemui.communal.widgets.WidgetConfigurator
import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialogFactory
@@ -154,6 +158,7 @@
fun CommunalHub(
modifier: Modifier = Modifier,
viewModel: BaseCommunalViewModel,
+ interactionHandler: RemoteViews.InteractionHandler? = null,
dialogFactory: SystemUIDialogFactory? = null,
widgetConfigurator: WidgetConfigurator? = null,
onOpenWidgetPicker: (() -> Unit)? = null,
@@ -173,6 +178,10 @@
derivedStateOf { selectedKey.value != null || reorderingWidgets }
}
val isEmptyState by viewModel.isEmptyState.collectAsStateWithLifecycle(initialValue = false)
+ val isCommunalContentVisible by
+ viewModel.isCommunalContentVisible.collectAsStateWithLifecycle(
+ initialValue = !viewModel.isEditMode
+ )
val contentPadding = gridContentPadding(viewModel.isEditMode, toolbarSize)
val contentOffset = beforeContentPadding(contentPadding).toOffset()
@@ -245,46 +254,88 @@
viewModel = viewModel,
)
} else {
- CommunalHubLazyGrid(
- communalContent = communalContent,
- viewModel = viewModel,
- contentPadding = contentPadding,
- contentOffset = contentOffset,
- setGridCoordinates = { gridCoordinates = it },
- updateDragPositionForRemove = { offset ->
- isPointerWithinEnabledRemoveButton(
- removeEnabled = removeButtonEnabled,
- offset = gridCoordinates?.let { it.positionInWindow() + offset },
- containerToCheck = removeButtonCoordinates
+ val slideOffsetInPx =
+ with(LocalDensity.current) { Dimensions.SlideOffsetY.toPx().toInt() }
+ AnimatedVisibility(
+ visible = isCommunalContentVisible,
+ enter =
+ fadeIn(
+ animationSpec =
+ tween(durationMillis = 83, delayMillis = 83, easing = LinearEasing)
+ ) +
+ slideInVertically(
+ animationSpec = tween(durationMillis = 1000, easing = Emphasized),
+ initialOffsetY = { -slideOffsetInPx }
+ ),
+ exit =
+ fadeOut(
+ animationSpec = tween(durationMillis = 167, easing = LinearEasing)
+ ) +
+ slideOutVertically(
+ animationSpec = tween(durationMillis = 1000, easing = Emphasized),
+ targetOffsetY = { -slideOffsetInPx }
+ ),
+ modifier = Modifier.fillMaxSize(),
+ ) {
+ Box {
+ CommunalHubLazyGrid(
+ communalContent = communalContent,
+ viewModel = viewModel,
+ contentPadding = contentPadding,
+ contentOffset = contentOffset,
+ setGridCoordinates = { gridCoordinates = it },
+ updateDragPositionForRemove = { offset ->
+ isPointerWithinEnabledRemoveButton(
+ removeEnabled = removeButtonEnabled,
+ offset =
+ gridCoordinates?.let { it.positionInWindow() + offset },
+ containerToCheck = removeButtonCoordinates
+ )
+ },
+ gridState = gridState,
+ contentListState = contentListState,
+ selectedKey = selectedKey,
+ widgetConfigurator = widgetConfigurator,
+ interactionHandler = interactionHandler,
)
- },
- gridState = gridState,
- contentListState = contentListState,
- selectedKey = selectedKey,
- widgetConfigurator = widgetConfigurator,
- )
+ }
+ }
}
}
- if (viewModel.isEditMode && onOpenWidgetPicker != null && onEditDone != null) {
- Toolbar(
- setToolbarSize = { toolbarSize = it },
- setRemoveButtonCoordinates = { removeButtonCoordinates = it },
- onEditDone = onEditDone,
- onOpenWidgetPicker = onOpenWidgetPicker,
- onRemoveClicked = {
- val index =
- selectedKey.value?.let { key ->
- contentListState.list.indexOfFirst { it.key == key }
+ if (onOpenWidgetPicker != null && onEditDone != null) {
+ AnimatedVisibility(
+ visible = viewModel.isEditMode && isCommunalContentVisible,
+ enter =
+ fadeIn(animationSpec = tween(durationMillis = 250, easing = LinearEasing)) +
+ slideInVertically(
+ animationSpec = tween(durationMillis = 1000, easing = Emphasized),
+ ),
+ exit =
+ fadeOut(animationSpec = tween(durationMillis = 167, easing = LinearEasing)) +
+ slideOutVertically(
+ animationSpec = tween(durationMillis = 1000, easing = Emphasized)
+ ),
+ ) {
+ Toolbar(
+ setToolbarSize = { toolbarSize = it },
+ setRemoveButtonCoordinates = { removeButtonCoordinates = it },
+ onEditDone = onEditDone,
+ onOpenWidgetPicker = onOpenWidgetPicker,
+ onRemoveClicked = {
+ val index =
+ selectedKey.value?.let { key ->
+ contentListState.list.indexOfFirst { it.key == key }
+ }
+ index?.let {
+ contentListState.onRemove(it)
+ contentListState.onSaveList()
+ viewModel.setSelectedKey(null)
}
- index?.let {
- contentListState.onRemove(it)
- contentListState.onSaveList()
- viewModel.setSelectedKey(null)
- }
- },
- removeEnabled = removeButtonEnabled
- )
+ },
+ removeEnabled = removeButtonEnabled
+ )
+ }
}
if (currentPopup == PopupType.CtaTile) {
PopupOnDismissCtaTile(viewModel::onHidePopup)
@@ -391,6 +442,7 @@
setGridCoordinates: (coordinates: LayoutCoordinates) -> Unit,
updateDragPositionForRemove: (offset: Offset) -> Boolean,
widgetConfigurator: WidgetConfigurator?,
+ interactionHandler: RemoteViews.InteractionHandler?,
) {
var gridModifier =
Modifier.align(Alignment.TopStart).onGloballyPositioned { setGridCoordinates(it) }
@@ -468,7 +520,8 @@
selected = selected && !isDragging,
widgetConfigurator = widgetConfigurator,
index = index,
- contentListState = contentListState
+ contentListState = contentListState,
+ interactionHandler = interactionHandler,
)
}
} else {
@@ -479,7 +532,8 @@
size = size,
selected = false,
index = index,
- contentListState = contentListState
+ contentListState = contentListState,
+ interactionHandler = interactionHandler,
)
}
}
@@ -578,24 +632,23 @@
)
.onSizeChanged { setToolbarSize(it) },
) {
- val spacerModifier = Modifier.width(ButtonDefaults.IconSpacing)
-
- if (!removeEnabled) {
- Button(
- modifier = Modifier.align(Alignment.CenterStart),
- onClick = onOpenWidgetPicker,
- colors = filledButtonColors(),
- contentPadding = Dimensions.ButtonPadding
- ) {
- Icon(Icons.Default.Add, stringResource(R.string.hub_mode_add_widget_button_text))
- Spacer(spacerModifier)
- Text(
- text = stringResource(R.string.hub_mode_add_widget_button_text),
- )
- }
+ ToolbarButton(
+ isPrimary = !removeEnabled,
+ modifier = Modifier.align(Alignment.CenterStart),
+ onClick = onOpenWidgetPicker,
+ ) {
+ Icon(Icons.Default.Add, stringResource(R.string.hub_mode_add_widget_button_text))
+ Text(
+ text = stringResource(R.string.hub_mode_add_widget_button_text),
+ )
}
- if (removeEnabled) {
+ AnimatedVisibility(
+ modifier = Modifier.align(Alignment.Center),
+ visible = removeEnabled,
+ enter = fadeIn(),
+ exit = fadeOut()
+ ) {
Button(
onClick = onRemoveClicked,
colors = filledButtonColors(),
@@ -603,33 +656,97 @@
modifier =
Modifier.graphicsLayer { alpha = removeButtonAlpha }
.onGloballyPositioned { setRemoveButtonCoordinates(it) }
- .align(Alignment.Center)
) {
- RemoveButtonContent(spacerModifier)
+ Row(
+ horizontalArrangement =
+ Arrangement.spacedBy(
+ ButtonDefaults.IconSpacing,
+ Alignment.CenterHorizontally
+ ),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(Icons.Default.Close, stringResource(R.string.button_to_remove_widget))
+ Text(
+ text = stringResource(R.string.button_to_remove_widget),
+ )
+ }
}
}
- if (!removeEnabled) {
- Button(
- modifier = Modifier.align(Alignment.CenterEnd),
- onClick = onEditDone,
- colors = filledButtonColors(),
- contentPadding = Dimensions.ButtonPadding
+ ToolbarButton(
+ isPrimary = !removeEnabled,
+ modifier = Modifier.align(Alignment.CenterEnd),
+ onClick = onEditDone,
+ ) {
+ Icon(
+ Icons.Default.Check,
+ stringResource(id = R.string.hub_mode_editing_exit_button_text)
+ )
+ Text(
+ text = stringResource(R.string.hub_mode_editing_exit_button_text),
+ )
+ }
+ }
+}
+
+/**
+ * Toolbar button that displays as a filled button if primary, and an outline button if secondary.
+ */
+@Composable
+private fun ToolbarButton(
+ isPrimary: Boolean = true,
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
+ content: @Composable RowScope.() -> Unit
+) {
+ val colors = LocalAndroidColorScheme.current
+ AnimatedVisibility(
+ visible = isPrimary,
+ modifier = modifier,
+ enter = fadeIn(),
+ exit = fadeOut()
+ ) {
+ Button(
+ onClick = onClick,
+ colors = filledButtonColors(),
+ contentPadding = Dimensions.ButtonPadding,
+ ) {
+ Row(
+ horizontalArrangement =
+ Arrangement.spacedBy(ButtonDefaults.IconSpacing, Alignment.CenterHorizontally),
+ verticalAlignment = Alignment.CenterVertically
) {
- Icon(
- Icons.Default.Check,
- stringResource(id = R.string.hub_mode_editing_exit_button_text)
- )
- Spacer(spacerModifier)
- Text(
- text = stringResource(R.string.hub_mode_editing_exit_button_text),
- )
+ content()
+ }
+ }
+ }
+
+ AnimatedVisibility(
+ visible = !isPrimary,
+ modifier = modifier,
+ enter = fadeIn(),
+ exit = fadeOut()
+ ) {
+ OutlinedButton(
+ onClick = onClick,
+ colors =
+ ButtonDefaults.outlinedButtonColors(
+ contentColor = colors.primary,
+ ),
+ border = BorderStroke(width = 2.0.dp, color = colors.primary),
+ contentPadding = Dimensions.ButtonPadding,
+ ) {
+ Row(
+ horizontalArrangement =
+ Arrangement.spacedBy(ButtonDefaults.IconSpacing, Alignment.CenterHorizontally),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ content()
}
}
}
}
-@OptIn(ExperimentalAnimationApi::class)
@Composable
private fun AnimatedVisibilityScope.ButtonToEditWidgets(
onClick: () -> Unit,
@@ -732,15 +849,6 @@
}
@Composable
-private fun RemoveButtonContent(spacerModifier: Modifier) {
- Icon(Icons.Default.Close, stringResource(R.string.button_to_remove_widget))
- Spacer(spacerModifier)
- Text(
- text = stringResource(R.string.button_to_remove_widget),
- )
-}
-
-@Composable
private fun filledButtonColors(): ButtonColors {
val colors = LocalAndroidColorScheme.current
return ButtonDefaults.buttonColors(
@@ -759,6 +867,7 @@
widgetConfigurator: WidgetConfigurator? = null,
index: Int,
contentListState: ContentListState,
+ interactionHandler: RemoteViews.InteractionHandler?,
) {
when (model) {
is CommunalContentModel.WidgetContent.Widget ->
@@ -778,7 +887,7 @@
is CommunalContentModel.WidgetContent.PendingWidget ->
PendingWidgetPlaceholder(model, modifier)
is CommunalContentModel.CtaTileInViewMode -> CtaTileInViewModeContent(viewModel, modifier)
- is CommunalContentModel.Smartspace -> SmartspaceContent(model, modifier)
+ is CommunalContentModel.Smartspace -> SmartspaceContent(interactionHandler, model, modifier)
is CommunalContentModel.Tutorial -> TutorialContent(modifier)
is CommunalContentModel.Umo -> Umo(viewModel, modifier)
}
@@ -1091,13 +1200,17 @@
@Composable
private fun SmartspaceContent(
+ interactionHandler: RemoteViews.InteractionHandler?,
model: CommunalContentModel.Smartspace,
modifier: Modifier = Modifier,
) {
AndroidView(
modifier = modifier,
factory = { context ->
- AppWidgetHostView(context).apply { updateAppWidget(model.remoteViews) }
+ SmartspaceAppWidgetHostView(context).apply {
+ interactionHandler?.let { setInteractionHandler(it) }
+ updateAppWidget(model.remoteViews)
+ }
},
// For reusing composition in lazy lists.
onReset = {},
@@ -1263,6 +1376,7 @@
horizontal = ToolbarButtonPaddingHorizontal,
)
val IconSize = 40.dp
+ val SlideOffsetY = 30.dp
}
private object Colors {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
index 9e905ac..94018bb 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
@@ -24,6 +24,7 @@
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
+import com.android.systemui.communal.widgets.WidgetInteractionHandler
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.ui.composable.ComposableScene
@@ -40,6 +41,7 @@
constructor(
private val viewModel: CommunalViewModel,
private val dialogFactory: SystemUIDialogFactory,
+ private val interactionHandler: WidgetInteractionHandler,
) : ComposableScene {
override val key = Scenes.Communal
@@ -53,6 +55,6 @@
@Composable
override fun SceneScope.Content(modifier: Modifier) {
- CommunalHub(modifier, viewModel, dialogFactory)
+ CommunalHub(modifier, viewModel, interactionHandler, dialogFactory)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/AmbientStatusBarSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/AmbientStatusBarSection.kt
new file mode 100644
index 0000000..3b335fa
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/AmbientStatusBarSection.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.ui.compose.section
+
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.FrameLayout
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.viewinterop.AndroidView
+import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.ambient.statusbar.dagger.AmbientStatusBarComponent
+import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarView
+import com.android.systemui.communal.ui.compose.Communal
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+class AmbientStatusBarSection
+@Inject
+constructor(
+ private val factory: AmbientStatusBarComponent.Factory,
+) {
+ @Composable
+ fun SceneScope.AmbientStatusBar(modifier: Modifier = Modifier) {
+ AndroidView(
+ factory = { context ->
+ (LayoutInflater.from(context)
+ .inflate(
+ /* resource = */ R.layout.ambient_status_bar_view,
+ /* root = */ FrameLayout(context),
+ /* attachToRoot = */ false,
+ ) as AmbientStatusBarView)
+ .apply {
+ visibility = View.VISIBLE
+ factory.create(this).getController().apply { init() }
+ }
+ },
+ modifier = modifier.element(Communal.Elements.StatusBar)
+ )
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
index 10c4030..68395b4 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
@@ -59,6 +59,13 @@
goneToShadeTransition(durationScale = 0.9)
}
from(Scenes.Gone, to = Scenes.QuickSettings) { goneToQuickSettingsTransition() }
+ from(
+ Scenes.Gone,
+ to = Scenes.QuickSettings,
+ key = SlightlyFasterShadeCollapse,
+ ) {
+ goneToQuickSettingsTransition(durationScale = 0.9)
+ }
from(Scenes.Gone, to = Scenes.QuickSettingsShade) { goneToQuickSettingsShadeTransition() }
from(Scenes.Lockscreen, to = Scenes.Bouncer) { lockscreenToBouncerTransition() }
from(Scenes.Lockscreen, to = Scenes.Communal) { lockscreenToCommunalTransition() }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
index ac3e015..b5a10ca 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
@@ -19,7 +19,10 @@
import android.view.ContextThemeWrapper
import android.view.ViewGroup
+import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.collectIsHoveredAsState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -32,7 +35,9 @@
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
+import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ColorScheme
+import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
@@ -40,6 +45,7 @@
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.graphics.graphicsLayer
@@ -58,6 +64,7 @@
import com.android.compose.animation.scene.TransitionState
import com.android.compose.animation.scene.ValueKey
import com.android.compose.animation.scene.animateElementFloatAsState
+import com.android.compose.modifiers.thenIf
import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.settingslib.Utils
import com.android.systemui.battery.BatteryMeterView
@@ -69,6 +76,7 @@
import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.ui.composable.ShadeHeader.Colors.onScrimDim
import com.android.systemui.shade.ui.composable.ShadeHeader.Dimensions.CollapsedHeight
import com.android.systemui.shade.ui.composable.ShadeHeader.Values.ClockScale
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
@@ -79,7 +87,6 @@
import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernShadeCarrierGroupMobileView
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.ShadeCarrierGroupMobileIconViewModel
import com.android.systemui.statusbar.policy.Clock
-import kotlin.math.max
object ShadeHeader {
object Elements {
@@ -103,6 +110,8 @@
object Colors {
val ColorScheme.shadeHeaderText: Color
get() = Color.White
+ val ColorScheme.onScrimDim: Color
+ get() = Color.DarkGray
}
object TestTags {
@@ -130,7 +139,7 @@
val horizontalPadding =
max(LocalScreenCornerRadius.current / 2f, Shade.Dimensions.HorizontalPadding)
- val useExpandedFormat by
+ val useExpandedTextFormat by
remember(cutoutLocation) {
derivedStateOf {
cutoutLocation != CutoutLocation.CENTER ||
@@ -138,6 +147,10 @@
}
}
+ val isLargeScreenLayout =
+ LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Medium ||
+ LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Expanded
+
val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsStateWithLifecycle()
// This layout assumes it is globally positioned at (0, 0) and is the
@@ -182,22 +195,22 @@
Modifier.element(ShadeHeader.Elements.CollapsedContentEnd)
.padding(horizontal = horizontalPadding)
) {
+ if (isLargeScreenLayout) {
+ ShadeCarrierGroup(
+ viewModel = viewModel,
+ modifier = Modifier.align(Alignment.CenterVertically),
+ )
+ }
SystemIconContainer(
+ viewModel = viewModel,
+ isClickable = isLargeScreenLayout,
modifier = Modifier.align(Alignment.CenterVertically)
) {
- when (LocalWindowSizeClass.current.widthSizeClass) {
- WindowWidthSizeClass.Medium,
- WindowWidthSizeClass.Expanded ->
- ShadeCarrierGroup(
- viewModel = viewModel,
- modifier = Modifier.align(Alignment.CenterVertically),
- )
- }
StatusIcons(
viewModel = viewModel,
createTintedIconManager = createTintedIconManager,
statusBarIconController = statusBarIconController,
- useExpandedFormat = useExpandedFormat,
+ useExpandedFormat = useExpandedTextFormat,
modifier =
Modifier.align(Alignment.CenterVertically)
.padding(end = 6.dp)
@@ -206,7 +219,7 @@
BatteryIcon(
createBatteryMeterViewController =
createBatteryMeterViewController,
- useExpandedFormat = useExpandedFormat,
+ useExpandedFormat = useExpandedTextFormat,
modifier = Modifier.align(Alignment.CenterVertically),
)
}
@@ -322,7 +335,7 @@
modifier = Modifier.widthIn(max = 90.dp).align(Alignment.CenterVertically),
)
Spacer(modifier = Modifier.weight(1f))
- SystemIconContainer {
+ SystemIconContainer(viewModel = viewModel, isClickable = false) {
StatusIcons(
viewModel = viewModel,
createTintedIconManager = createTintedIconManager,
@@ -531,12 +544,30 @@
@Composable
private fun SystemIconContainer(
+ viewModel: ShadeHeaderViewModel,
+ isClickable: Boolean,
modifier: Modifier = Modifier,
content: @Composable RowScope.() -> Unit
) {
- // TODO(b/298524053): add hover state for this container
+ val interactionSource = remember { MutableInteractionSource() }
+ val isHovered by interactionSource.collectIsHoveredAsState()
+
+ val hoverModifier = Modifier
+ .clip(RoundedCornerShape(CollapsedHeight / 4))
+ .background(MaterialTheme.colorScheme.onScrimDim)
+
Row(
- modifier = modifier.height(CollapsedHeight),
+ modifier = modifier
+ .height(CollapsedHeight)
+ .padding(vertical = CollapsedHeight / 4)
+ .thenIf(isClickable) {
+ Modifier.clickable(
+ interactionSource = interactionSource,
+ indication = null,
+ onClick = { viewModel.onSystemIconContainerClicked() },
+ )
+ }
+ .thenIf(isHovered) { hoverModifier },
content = content,
)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
index e1ae80f..6d03118 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
@@ -88,7 +88,7 @@
} else {
MaterialTheme.colorScheme.surface
},
- shape = RoundedCornerShape(28.dp),
+ shape = RoundedCornerShape(20.dp),
contentColor =
if (viewModel.isActive) {
MaterialTheme.colorScheme.onTertiaryContainer
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
index 1b821d3..bb2daec 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
@@ -91,7 +91,7 @@
contentDescription = label
},
onClick = { onCheckedChange(!viewModel.isActive) },
- shape = RoundedCornerShape(28.dp),
+ shape = RoundedCornerShape(20.dp),
colors = colors,
contentPadding = PaddingValues(0.dp)
) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
index 9891b5b..3295dde 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
@@ -17,6 +17,7 @@
package com.android.systemui.volume.panel.component.spatialaudio.ui.composable
import android.view.Gravity
+import androidx.annotation.VisibleForTesting
import androidx.compose.foundation.basicMarquee
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
@@ -71,7 +72,8 @@
}
@Composable
- private fun Content(dialog: SystemUIDialog) {
+ @VisibleForTesting
+ fun Content(dialog: SystemUIDialog) {
val isAvailable by viewModel.isAvailable.collectAsStateWithLifecycle()
if (!isAvailable) {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
index 7fd3a176..114dcf4 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
@@ -381,18 +381,17 @@
// relayout/redraw for nothing.
fromValue
} else {
- // In the case of bouncing, if the value remains constant during the overscroll, we
- // should use the value of the scene we are bouncing around.
- if (!canOverflow && transition is TransitionState.HasOverscrollProperties) {
- val bouncingScene = transition.bouncingScene
- if (bouncingScene != null) {
- return sharedValue[bouncingScene]
- }
- }
-
+ val overscrollSpec = transition.currentOverscrollSpec
val progress =
- if (canOverflow) transition.progress
- else transition.progress.fastCoerceIn(0f, 1f)
+ when {
+ overscrollSpec == null -> {
+ if (canOverflow) transition.progress
+ else transition.progress.fastCoerceIn(0f, 1f)
+ }
+ overscrollSpec.scene == transition.toScene -> 1f
+ else -> 0f
+ }
+
sharedValue.type.lerp(fromValue, toValue, progress)
}
} else fromValue ?: toValue
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
index 48a348b..c2dd803 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
@@ -109,8 +109,7 @@
layoutState.transitions.interruptionHandler.onInterruption(
transitionState,
target,
- )
- ?: DefaultInterruptionHandler.onInterruption(transitionState, target)
+ ) ?: DefaultInterruptionHandler.onInterruption(transitionState, target)
val animateFrom = interruptionResult.animateFrom
if (
@@ -159,6 +158,7 @@
val transition =
if (reversed) {
OneOffTransition(
+ key = transitionKey,
fromScene = targetScene,
toScene = fromScene,
currentScene = targetScene,
@@ -167,6 +167,7 @@
)
} else {
OneOffTransition(
+ key = transitionKey,
fromScene = fromScene,
toScene = targetScene,
currentScene = targetScene,
@@ -178,7 +179,7 @@
// Change the current layout state to start this new transition. This will compute the
// TransformationSpec associated to this transition, which we need to initialize the Animatable
// that will actually animate it.
- layoutState.startTransition(transition, transitionKey, chain)
+ layoutState.startTransition(transition, chain)
// The transition now contains the transformation spec that we should use to instantiate the
// Animatable.
@@ -207,6 +208,7 @@
}
private class OneOffTransition(
+ override val key: TransitionKey?,
fromScene: SceneKey,
toScene: SceneKey,
override val currentScene: SceneKey,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
index e9633c2..60d78fe 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
@@ -257,7 +257,7 @@
fun updateTransition(newTransition: SwipeTransition, force: Boolean = false) {
if (isDrivingTransition || force) {
- layoutState.startTransition(newTransition, newTransition.key)
+ layoutState.startTransition(newTransition)
}
swipeTransition = newTransition
@@ -555,7 +555,7 @@
val layoutImpl: SceneTransitionLayoutImpl,
val layoutState: BaseSceneTransitionLayoutState,
val coroutineScope: CoroutineScope,
- val key: TransitionKey?,
+ override val key: TransitionKey?,
val _fromScene: Scene,
val _toScene: Scene,
val userActionDistanceScope: UserActionDistanceScope,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index edf8943..5611c6e 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -39,6 +39,8 @@
import androidx.compose.ui.layout.Placeable
import androidx.compose.ui.node.DrawModifierNode
import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.TraversableNode
+import androidx.compose.ui.node.traverseDescendants
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.IntSize
@@ -165,7 +167,7 @@
private var currentTransitions: List<TransitionState.Transition>,
private var scene: Scene,
private var key: ElementKey,
-) : Modifier.Node(), DrawModifierNode, ApproachLayoutModifierNode {
+) : Modifier.Node(), DrawModifierNode, ApproachLayoutModifierNode, TraversableNode {
private var _element: Element? = null
private val element: Element
get() = _element!!
@@ -174,6 +176,8 @@
private val sceneState: Element.SceneState
get() = _sceneState!!
+ override val traverseKey: Any = ElementTraverseKey
+
override fun onAttach() {
super.onAttach()
updateElementAndSceneValues()
@@ -280,7 +284,7 @@
constraints: Constraints,
): MeasureResult {
val transitions = currentTransitions
- val transition = elementTransition(element, transitions)
+ val transition = elementTransition(layoutImpl, element, transitions)
// If this element is not supposed to be laid out now, either because it is not part of any
// ongoing transition or the other scene of its transition is overscrolling, then lay out
@@ -289,18 +293,15 @@
val isOtherSceneOverscrolling = overscrollScene != null && overscrollScene != scene.key
val isNotPartOfAnyOngoingTransitions = transitions.isNotEmpty() && transition == null
if (isNotPartOfAnyOngoingTransitions || isOtherSceneOverscrolling) {
- sceneState.lastOffset = Offset.Unspecified
- sceneState.lastScale = Scale.Unspecified
- sceneState.lastAlpha = Element.AlphaUnspecified
+ recursivelyClearPlacementValues()
+ sceneState.lastSize = Element.SizeUnspecified
val placeable = measurable.measure(constraints)
- sceneState.lastSize = placeable.size()
-
return layout(placeable.width, placeable.height) { /* Do not place */ }
}
val placeable =
- measure(layoutImpl, scene, element, transition, sceneState, measurable, constraints)
+ measure(layoutImpl, element, transition, sceneState, measurable, constraints)
sceneState.lastSize = placeable.size()
return layout(placeable.width, placeable.height) { place(transition, placeable) }
}
@@ -315,16 +316,10 @@
// scene when idle.
val coords =
coordinates ?: error("Element ${element.key} does not have any coordinates")
- val targetOffsetInScene = lookaheadScopeCoordinates.localLookaheadPositionOf(coords)
// No need to place the element in this scene if we don't want to draw it anyways.
- if (!shouldPlaceElement(layoutImpl, scene, element, transition)) {
- sceneState.lastOffset = Offset.Unspecified
- sceneState.lastScale = Scale.Unspecified
- sceneState.lastAlpha = Element.AlphaUnspecified
-
- sceneState.clearValuesBeforeInterruption()
- sceneState.clearInterruptionDeltas()
+ if (!shouldPlaceElement(layoutImpl, scene.key, element, transition)) {
+ recursivelyClearPlacementValues()
return
}
@@ -332,12 +327,11 @@
val targetOffset =
computeValue(
layoutImpl,
- scene,
+ sceneState,
element,
transition,
sceneValue = { it.targetOffset },
transformation = { it.offset },
- idleValue = targetOffsetInScene,
currentValue = { currentOffset },
isSpecified = { it != Offset.Unspecified },
::lerp,
@@ -353,7 +347,17 @@
getValueBeforeInterruption = { sceneState.offsetBeforeInterruption },
setValueBeforeInterruption = { sceneState.offsetBeforeInterruption = it },
getInterruptionDelta = { sceneState.offsetInterruptionDelta },
- setInterruptionDelta = { sceneState.offsetInterruptionDelta = it },
+ setInterruptionDelta = { delta ->
+ setPlacementInterruptionDelta(
+ element = element,
+ sceneState = sceneState,
+ transition = transition,
+ delta = delta,
+ setter = { sceneState, delta ->
+ sceneState.offsetInterruptionDelta = delta
+ },
+ )
+ },
diff = { a, b -> a - b },
add = { a, b, bProgress -> a + b * bProgress },
)
@@ -363,7 +367,7 @@
val offset = (interruptedOffset - currentOffset).round()
if (
isElementOpaque(scene, element, transition) &&
- interruptedAlpha(layoutImpl, transition, sceneState, alpha = 1f) == 1f
+ interruptedAlpha(layoutImpl, element, transition, sceneState, alpha = 1f) == 1f
) {
sceneState.lastAlpha = 1f
@@ -374,28 +378,51 @@
} else {
placeable.placeWithLayer(offset) {
// This layer might still run on its own (outside of the placement phase) even
- // if this element is not placed anymore, so we need to double check again here
- // before calling [elementAlpha] (which will update [SceneState.lastAlpha]). We
- // also need to recompute the current transition to make sure that we are using
- // the current transition and not a reference to an old one. See b/343138966 for
- // details.
- val transition = elementTransition(element, currentTransitions)
- if (!shouldPlaceElement(layoutImpl, scene, element, transition)) {
+ // if this element is not placed or composed anymore, so we need to double check
+ // again here before calling [elementAlpha] (which will update
+ // [SceneState.lastAlpha]). We also need to recompute the current transition to
+ // make sure that we are using the current transition and not a reference to an
+ // old one. See b/343138966 for details.
+ if (_element == null) {
return@placeWithLayer
}
- alpha = elementAlpha(layoutImpl, scene, element, transition, sceneState)
+ val transition = elementTransition(layoutImpl, element, currentTransitions)
+ if (!shouldPlaceElement(layoutImpl, scene.key, element, transition)) {
+ return@placeWithLayer
+ }
+
+ alpha = elementAlpha(layoutImpl, element, transition, sceneState)
compositingStrategy = CompositingStrategy.ModulateAlpha
}
}
}
}
+ /**
+ * Recursively clear the last placement values on this node and all descendants ElementNodes.
+ * This should be called when this node is not placed anymore, so that we correctly clear values
+ * for the descendants for which approachMeasure() won't be called.
+ */
+ private fun recursivelyClearPlacementValues() {
+ fun Element.SceneState.clearLastPlacementValues() {
+ lastOffset = Offset.Unspecified
+ lastScale = Scale.Unspecified
+ lastAlpha = Element.AlphaUnspecified
+ }
+
+ sceneState.clearLastPlacementValues()
+ traverseDescendants(ElementTraverseKey) { node ->
+ (node as ElementNode).sceneState.clearLastPlacementValues()
+ TraversableNode.Companion.TraverseDescendantsAction.ContinueTraversal
+ }
+ }
+
override fun ContentDrawScope.draw() {
element.wasDrawnInAnyScene = true
- val transition = elementTransition(element, currentTransitions)
- val drawScale = getDrawScale(layoutImpl, scene, element, transition, sceneState)
+ val transition = elementTransition(layoutImpl, element, currentTransitions)
+ val drawScale = getDrawScale(layoutImpl, element, transition, sceneState)
if (drawScale == Scale.Default) {
drawContent()
} else {
@@ -410,6 +437,8 @@
}
companion object {
+ private val ElementTraverseKey = Any()
+
private fun maybePruneMaps(
layoutImpl: SceneTransitionLayoutImpl,
element: Element,
@@ -435,6 +464,7 @@
* its scenes contains the element.
*/
private fun elementTransition(
+ layoutImpl: SceneTransitionLayoutImpl,
element: Element,
transitions: List<TransitionState.Transition>,
): TransitionState.Transition? {
@@ -448,7 +478,7 @@
if (transition != previousTransition && transition != null && previousTransition != null) {
// The previous transition was interrupted by another transition.
- prepareInterruption(element, transition, previousTransition)
+ prepareInterruption(layoutImpl, element, transition, previousTransition)
} else if (transition == null && previousTransition != null) {
// The transition was just finished.
element.sceneStates.values.forEach {
@@ -461,18 +491,44 @@
}
private fun prepareInterruption(
+ layoutImpl: SceneTransitionLayoutImpl,
element: Element,
transition: TransitionState.Transition,
previousTransition: TransitionState.Transition,
) {
val sceneStates = element.sceneStates
- sceneStates[previousTransition.fromScene]?.selfUpdateValuesBeforeInterruption()
- sceneStates[previousTransition.toScene]?.selfUpdateValuesBeforeInterruption()
- sceneStates[transition.fromScene]?.selfUpdateValuesBeforeInterruption()
- sceneStates[transition.toScene]?.selfUpdateValuesBeforeInterruption()
+ fun updatedSceneState(key: SceneKey): Element.SceneState? {
+ return sceneStates[key]?.also { it.selfUpdateValuesBeforeInterruption() }
+ }
+
+ val previousFromState = updatedSceneState(previousTransition.fromScene)
+ val previousToState = updatedSceneState(previousTransition.toScene)
+ val fromState = updatedSceneState(transition.fromScene)
+ val toState = updatedSceneState(transition.toScene)
reconcileStates(element, previousTransition)
reconcileStates(element, transition)
+
+ // Remove the interruption values to all scenes but the scene(s) where the element will be
+ // placed, to make sure that interruption deltas are computed only right after this interruption
+ // is prepared.
+ fun cleanInterruptionValues(sceneState: Element.SceneState) {
+ sceneState.sizeInterruptionDelta = IntSize.Zero
+ sceneState.offsetInterruptionDelta = Offset.Zero
+ sceneState.alphaInterruptionDelta = 0f
+ sceneState.scaleInterruptionDelta = Scale.Zero
+
+ if (!shouldPlaceElement(layoutImpl, sceneState.scene, element, transition)) {
+ sceneState.offsetBeforeInterruption = Offset.Unspecified
+ sceneState.alphaBeforeInterruption = Element.AlphaUnspecified
+ sceneState.scaleBeforeInterruption = Scale.Unspecified
+ }
+ }
+
+ previousFromState?.let { cleanInterruptionValues(it) }
+ previousToState?.let { cleanInterruptionValues(it) }
+ fromState?.let { cleanInterruptionValues(it) }
+ toState?.let { cleanInterruptionValues(it) }
}
/**
@@ -579,9 +635,38 @@
}
}
+/**
+ * Set the interruption delta of a *placement/drawing*-related value (offset, alpha, scale). This
+ * ensures that the delta is also set on the other scene in the transition for shared elements, so
+ * that there is no jump cut if the scene where the element is placed has changed.
+ */
+private inline fun <T> setPlacementInterruptionDelta(
+ element: Element,
+ sceneState: Element.SceneState,
+ transition: TransitionState.Transition?,
+ delta: T,
+ setter: (Element.SceneState, T) -> Unit,
+) {
+ // Set the interruption delta on the current scene.
+ setter(sceneState, delta)
+
+ if (transition == null) {
+ return
+ }
+
+ // If the element is shared, also set the delta on the other scene so that it is used by that
+ // scene if we start overscrolling it and change the scene where the element is placed.
+ val otherScene =
+ if (sceneState.scene == transition.fromScene) transition.toScene else transition.fromScene
+ val otherSceneState = element.sceneStates[otherScene] ?: return
+ if (isSharedElementEnabled(element.key, transition)) {
+ setter(otherSceneState, delta)
+ }
+}
+
private fun shouldPlaceElement(
layoutImpl: SceneTransitionLayoutImpl,
- scene: Scene,
+ scene: SceneKey,
element: Element,
transition: TransitionState.Transition?,
): Boolean {
@@ -592,7 +677,7 @@
// Don't place the element in this scene if this scene is not part of the current element
// transition.
- if (scene.key != transition.fromScene && scene.key != transition.toScene) {
+ if (scene != transition.fromScene && scene != transition.toScene) {
return false
}
@@ -610,7 +695,7 @@
return shouldPlaceOrComposeSharedElement(
layoutImpl,
- scene.key,
+ scene,
element.key,
transition,
)
@@ -714,7 +799,6 @@
*/
private fun elementAlpha(
layoutImpl: SceneTransitionLayoutImpl,
- scene: Scene,
element: Element,
transition: TransitionState.Transition?,
sceneState: Element.SceneState,
@@ -722,12 +806,11 @@
val alpha =
computeValue(
layoutImpl,
- scene,
+ sceneState,
element,
transition,
sceneValue = { 1f },
transformation = { it.alpha },
- idleValue = 1f,
currentValue = { 1f },
isSpecified = { true },
::lerp,
@@ -740,13 +823,14 @@
element.sceneStates.forEach { it.value.alphaBeforeInterruption = 0f }
}
- val interruptedAlpha = interruptedAlpha(layoutImpl, transition, sceneState, alpha)
+ val interruptedAlpha = interruptedAlpha(layoutImpl, element, transition, sceneState, alpha)
sceneState.lastAlpha = interruptedAlpha
return interruptedAlpha
}
private fun interruptedAlpha(
layoutImpl: SceneTransitionLayoutImpl,
+ element: Element,
transition: TransitionState.Transition?,
sceneState: Element.SceneState,
alpha: Float,
@@ -760,15 +844,22 @@
getValueBeforeInterruption = { sceneState.alphaBeforeInterruption },
setValueBeforeInterruption = { sceneState.alphaBeforeInterruption = it },
getInterruptionDelta = { sceneState.alphaInterruptionDelta },
- setInterruptionDelta = { sceneState.alphaInterruptionDelta = it },
+ setInterruptionDelta = { delta ->
+ setPlacementInterruptionDelta(
+ element = element,
+ sceneState = sceneState,
+ transition = transition,
+ delta = delta,
+ setter = { sceneState, delta -> sceneState.alphaInterruptionDelta = delta },
+ )
+ },
diff = { a, b -> a - b },
add = { a, b, bProgress -> a + b * bProgress },
)
}
-private fun ApproachMeasureScope.measure(
+private fun measure(
layoutImpl: SceneTransitionLayoutImpl,
- scene: Scene,
element: Element,
transition: TransitionState.Transition?,
sceneState: Element.SceneState,
@@ -783,12 +874,11 @@
val targetSize =
computeValue(
layoutImpl,
- scene,
+ sceneState,
element,
transition,
sceneValue = { it.targetSize },
transformation = { it.size },
- idleValue = lookaheadSize,
currentValue = { measurable.measure(constraints).also { maybePlaceable = it }.size() },
isSpecified = { it != Element.SizeUnspecified },
::lerp,
@@ -834,7 +924,6 @@
private fun ContentDrawScope.getDrawScale(
layoutImpl: SceneTransitionLayoutImpl,
- scene: Scene,
element: Element,
transition: TransitionState.Transition?,
sceneState: Element.SceneState,
@@ -842,12 +931,11 @@
val scale =
computeValue(
layoutImpl,
- scene,
+ sceneState,
element,
transition,
sceneValue = { Scale.Default },
transformation = { it.drawScale },
- idleValue = Scale.Default,
currentValue = { Scale.Default },
isSpecified = { true },
::lerp,
@@ -867,7 +955,15 @@
getValueBeforeInterruption = { sceneState.scaleBeforeInterruption },
setValueBeforeInterruption = { sceneState.scaleBeforeInterruption = it },
getInterruptionDelta = { sceneState.scaleInterruptionDelta },
- setInterruptionDelta = { sceneState.scaleInterruptionDelta = it },
+ setInterruptionDelta = { delta ->
+ setPlacementInterruptionDelta(
+ element = element,
+ sceneState = sceneState,
+ transition = transition,
+ delta = delta,
+ setter = { sceneState, delta -> sceneState.scaleInterruptionDelta = delta },
+ )
+ },
diff = { a, b ->
Scale(
scaleX = a.scaleX - b.scaleX,
@@ -906,11 +1002,12 @@
* Measurable.
*
* @param layoutImpl the [SceneTransitionLayoutImpl] associated to [element].
- * @param scene the scene containing [element].
+ * @param currentSceneState the scene state of the scene for which we are computing the value. Note
+ * that during interruptions, this could be the state of a scene that is neither
+ * [transition.toScene] nor [transition.fromScene].
* @param element the element being animated.
* @param sceneValue the value being animated.
* @param transformation the transformation associated to the value being animated.
- * @param idleValue the value when idle, i.e. when there is no transition happening.
* @param currentValue the value that would be used if it is not transformed. Note that this is
* different than [idleValue] even if the value is not transformed directly because it could be
* impacted by the transformations on other elements, like a parent that is being translated or
@@ -920,12 +1017,11 @@
*/
private inline fun <T> computeValue(
layoutImpl: SceneTransitionLayoutImpl,
- scene: Scene,
+ currentSceneState: Element.SceneState,
element: Element,
transition: TransitionState.Transition?,
sceneValue: (Element.SceneState) -> T,
transformation: (ElementTransformations) -> PropertyTransformation<T>?,
- idleValue: T,
currentValue: () -> T,
isSpecified: (T) -> Boolean,
lerp: (T, T, Float) -> T,
@@ -947,19 +1043,22 @@
if (fromState == null && toState == null) {
// TODO(b/311600838): Throw an exception instead once layers of disposed elements are not
// run anymore.
- return idleValue
+ return sceneValue(currentSceneState)
}
+ val currentScene = currentSceneState.scene
if (transition is TransitionState.HasOverscrollProperties) {
val overscroll = transition.currentOverscrollSpec
- if (overscroll?.scene == scene.key) {
- val elementSpec = overscroll.transformationSpec.transformations(element.key, scene.key)
+ if (overscroll?.scene == currentScene) {
+ val elementSpec =
+ overscroll.transformationSpec.transformations(element.key, currentScene)
val propertySpec = transformation(elementSpec) ?: return currentValue()
- val overscrollState = checkNotNull(if (scene.key == toScene) toState else fromState)
+ val overscrollState = checkNotNull(if (currentScene == toScene) toState else fromState)
+ val idleValue = sceneValue(overscrollState)
val targetValue =
propertySpec.transform(
layoutImpl,
- scene,
+ currentScene,
element,
overscrollState,
transition,
@@ -1003,24 +1102,30 @@
return if (start == end) start else lerp(start, end, transition.progress)
}
- val transformation =
- transformation(transition.transformationSpec.transformations(element.key, scene.key))
- // If there is no transformation explicitly associated to this element value, let's use
- // the value given by the system (like the current position and size given by the layout
- // pass).
- ?: return currentValue()
-
// Get the transformed value, i.e. the target value at the beginning (for entering elements) or
// end (for leaving elements) of the transition.
val sceneState =
checkNotNull(
when {
- isSharedElement && scene.key == fromScene -> fromState
+ isSharedElement && currentScene == fromScene -> fromState
isSharedElement -> toState
else -> fromState ?: toState
}
)
+ // The scene for which we compute the transformation. Note that this is not necessarily
+ // [currentScene] because [currentScene] could be a different scene than the transition
+ // fromScene or toScene during interruptions.
+ val scene = sceneState.scene
+
+ val transformation =
+ transformation(transition.transformationSpec.transformations(element.key, scene))
+ // If there is no transformation explicitly associated to this element value, let's use
+ // the value given by the system (like the current position and size given by the layout
+ // pass).
+ ?: return currentValue()
+
+ val idleValue = sceneValue(sceneState)
val targetValue =
transformation.transform(
layoutImpl,
@@ -1042,7 +1147,7 @@
val rangeProgress = transformation.range?.progress(progress) ?: progress
// Interpolate between the value at rest and the value before entering/after leaving.
- val isEntering = scene.key == toScene
+ val isEntering = scene == toScene
return if (isEntering) {
lerp(targetValue, idleValue, rangeProgress)
} else {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
index 3cc8431..6001f1f 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
@@ -19,8 +19,6 @@
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.awaitHorizontalTouchSlopOrCancellation
import androidx.compose.foundation.gestures.awaitVerticalTouchSlopOrCancellation
-import androidx.compose.foundation.gestures.horizontalDrag
-import androidx.compose.foundation.gestures.verticalDrag
import androidx.compose.runtime.Stable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
@@ -32,7 +30,9 @@
import androidx.compose.ui.input.pointer.PointerInputScope
import androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNode
import androidx.compose.ui.input.pointer.changedToDownIgnoreConsumed
+import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed
import androidx.compose.ui.input.pointer.positionChange
+import androidx.compose.ui.input.pointer.positionChangeIgnoreConsumed
import androidx.compose.ui.input.pointer.util.VelocityTracker
import androidx.compose.ui.input.pointer.util.addPointerInputChange
import androidx.compose.ui.node.CompositionLocalConsumerModifierNode
@@ -46,6 +46,8 @@
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.util.fastAll
+import androidx.compose.ui.util.fastAny
+import androidx.compose.ui.util.fastFirstOrNull
import androidx.compose.ui.util.fastForEach
import kotlin.coroutines.cancellation.CancellationException
import kotlin.math.sign
@@ -236,8 +238,23 @@
onDragCancel: (controller: DragController) -> Unit,
swipeDetector: SwipeDetector,
) {
- // Wait for a consumable event in [PointerEventPass.Main] pass
- val consumablePointer = awaitConsumableEvent().changes.first()
+ val consumablePointer =
+ awaitConsumableEvent {
+ // We are searching for an event that can be used as the starting point for the
+ // drag gesture. Our options are:
+ // - Initial: These events should never be consumed by the MultiPointerDraggable
+ // since our ancestors can consume the gesture, but we would eliminate this
+ // possibility for our descendants.
+ // - Main: These events are consumed during the drag gesture, and they are a
+ // good place to start if the previous event has not been consumed.
+ // - Final: If the previous event has been consumed, we can wait for the Main
+ // pass to finish. If none of our ancestors were interested in the event, we
+ // can wait for an unconsumed event in the Final pass.
+ val previousConsumed = currentEvent.changes.fastAny { it.isConsumed }
+ if (previousConsumed) PointerEventPass.Final else PointerEventPass.Main
+ }
+ .changes
+ .first()
var overSlop = 0f
val drag =
@@ -297,18 +314,22 @@
onDrag(controller, drag, overSlop)
successful =
- when (orientation) {
- Orientation.Horizontal ->
- horizontalDrag(drag.id) {
- onDrag(controller, it, it.positionChange().toFloat())
- it.consume()
- }
- Orientation.Vertical ->
- verticalDrag(drag.id) {
- onDrag(controller, it, it.positionChange().toFloat())
- it.consume()
- }
- }
+ drag(
+ initialPointerId = drag.id,
+ hasDragged = { it.positionChangeIgnoreConsumed().toFloat() != 0f },
+ onDrag = {
+ onDrag(controller, it, it.positionChange().toFloat())
+ it.consume()
+ },
+ onIgnoredEvent = {
+ // We are still dragging an object, but this event is not of interest to
+ // the caller.
+ // This event will not trigger the onDrag event, but we will consume the
+ // event to prevent another pointerInput from interrupting the current
+ // gesture just because the event was ignored.
+ it.consume()
+ },
+ )
} catch (t: Throwable) {
onDragCancel(controller)
throw t
@@ -322,7 +343,9 @@
}
}
- private suspend fun AwaitPointerEventScope.awaitConsumableEvent(): PointerEvent {
+ private suspend fun AwaitPointerEventScope.awaitConsumableEvent(
+ pass: () -> PointerEventPass,
+ ): PointerEvent {
fun canBeConsumed(changes: List<PointerInputChange>): Boolean {
// All pointers must be:
return changes.fastAll {
@@ -337,9 +360,7 @@
var event: PointerEvent
do {
- // To allow the descendants with the opportunity to consume the event, we wait for it in
- // the Main pass.
- event = awaitPointerEvent()
+ event = awaitPointerEvent(pass = pass())
} while (!canBeConsumed(event.changes))
// We found a consumable event in the Main pass
@@ -352,4 +373,82 @@
Orientation.Horizontal -> x
}
}
+
+ /**
+ * Continues to read drag events until all pointers are up or the drag event is canceled. The
+ * initial pointer to use for driving the drag is [initialPointerId]. [hasDragged] passes the
+ * result whether a change was detected from the drag function or not.
+ *
+ * Whenever the pointer moves, if [hasDragged] returns true, [onDrag] is called; otherwise,
+ * [onIgnoredEvent] is called.
+ *
+ * @return true when gesture ended with all pointers up and false when the gesture was canceled.
+ *
+ * Note: Inspired by DragGestureDetector.kt
+ */
+ private suspend inline fun AwaitPointerEventScope.drag(
+ initialPointerId: PointerId,
+ hasDragged: (PointerInputChange) -> Boolean,
+ onDrag: (PointerInputChange) -> Unit,
+ onIgnoredEvent: (PointerInputChange) -> Unit,
+ ): Boolean {
+ val pointer = currentEvent.changes.fastFirstOrNull { it.id == initialPointerId }
+ val isPointerUp = pointer?.pressed != true
+ if (isPointerUp) {
+ return false // The pointer has already been lifted, so the gesture is canceled
+ }
+ var pointerId = initialPointerId
+ while (true) {
+ val change = awaitDragOrUp(pointerId, hasDragged, onIgnoredEvent) ?: return false
+
+ if (change.isConsumed) {
+ return false
+ }
+
+ if (change.changedToUpIgnoreConsumed()) {
+ return true
+ }
+
+ onDrag(change)
+ pointerId = change.id
+ }
+ }
+
+ /**
+ * Waits for a single drag in one axis, final pointer up, or all pointers are up. When
+ * [initialPointerId] has lifted, another pointer that is down is chosen to be the finger
+ * governing the drag. When the final pointer is lifted, that [PointerInputChange] is returned.
+ * When a drag is detected, that [PointerInputChange] is returned. A drag is only detected when
+ * [hasDragged] returns `true`. Events that should not be captured are passed to
+ * [onIgnoredEvent].
+ *
+ * `null` is returned if there was an error in the pointer input stream and the pointer that was
+ * down was dropped before the 'up' was received.
+ *
+ * Note: Inspired by DragGestureDetector.kt
+ */
+ private suspend inline fun AwaitPointerEventScope.awaitDragOrUp(
+ initialPointerId: PointerId,
+ hasDragged: (PointerInputChange) -> Boolean,
+ onIgnoredEvent: (PointerInputChange) -> Unit,
+ ): PointerInputChange? {
+ var pointerId = initialPointerId
+ while (true) {
+ val event = awaitPointerEvent()
+ val dragEvent = event.changes.fastFirstOrNull { it.id == pointerId } ?: return null
+ if (dragEvent.changedToUpIgnoreConsumed()) {
+ val otherDown = event.changes.fastFirstOrNull { it.pressed }
+ if (otherDown == null) {
+ // This is the last "up"
+ return dragEvent
+ } else {
+ pointerId = otherDown.id
+ }
+ } else if (hasDragged(dragEvent)) {
+ return dragEvent
+ } else {
+ onIgnoredEvent(dragEvent)
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
index f32720c..7ea8cbd 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
@@ -293,7 +293,15 @@
width = fromSize.width
height = fromSize.height
} else {
- val size = lerp(fromSize, toSize, transition.progress)
+ val overscrollSpec = transition.currentOverscrollSpec
+ val progress =
+ when {
+ overscrollSpec == null -> transition.progress
+ overscrollSpec.scene == transition.toScene -> 1f
+ else -> 0f
+ }
+
+ val size = lerp(fromSize, toSize, progress)
width = size.width.coerceAtLeast(0)
height = size.height.coerceAtLeast(0)
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index 44affd9..a8df6f4 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -226,6 +226,12 @@
val toScene: SceneKey,
) : TransitionState {
/**
+ * The key of this transition. This should usually be null, but it can be specified to use a
+ * specific set of transformations associated to this transition.
+ */
+ open val key: TransitionKey? = null
+
+ /**
* The progress of the transition. This is usually in the `[0; 1]` range, but it can also be
* less than `0` or greater than `1` when using transitions with a spring AnimationSpec or
* when flinging quickly during a swipe gesture.
@@ -455,11 +461,7 @@
*
* Important: you *must* call [finishTransition] once the transition is finished.
*/
- internal fun startTransition(
- transition: TransitionState.Transition,
- transitionKey: TransitionKey? = null,
- chain: Boolean = true,
- ) {
+ internal fun startTransition(transition: TransitionState.Transition, chain: Boolean = true) {
checkThread()
// Compute the [TransformationSpec] when the transition starts.
@@ -469,7 +471,9 @@
// Update the transition specs.
transition.transformationSpec =
- transitions.transitionSpec(fromScene, toScene, key = transitionKey).transformationSpec()
+ transitions
+ .transitionSpec(fromScene, toScene, key = transition.key)
+ .transformationSpec()
if (orientation != null) {
transition.updateOverscrollSpecs(
fromSpec = transitions.overscrollSpec(fromScene, orientation),
@@ -568,9 +572,10 @@
originalTransition = transitionState,
fromScene = targetCurrentScene,
toScene = matchingLink.targetTo,
+ key = matchingLink.targetTransitionKey,
)
- stateLink.target.startTransition(linkedTransition, matchingLink.targetTransitionKey)
+ stateLink.target.startTransition(linkedTransition)
activeTransitionLinks[stateLink] = linkedTransition
}
}
@@ -763,7 +768,7 @@
/** A [MutableSceneTransitionLayoutState] that holds the value for the current scene. */
internal class MutableSceneTransitionLayoutStateImpl(
initialScene: SceneKey,
- override var transitions: SceneTransitions,
+ override var transitions: SceneTransitions = transitions {},
private val canChangeScene: (SceneKey) -> Boolean = { true },
stateLinks: List<StateLink> = emptyList(),
enableInterruptions: Boolean = DEFAULT_INTERRUPTIONS_ENABLED,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
index b54afae..73ee451 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
@@ -20,7 +20,6 @@
import com.android.compose.animation.scene.Element
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.Scene
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneTransitionLayoutImpl
import com.android.compose.animation.scene.TransitionState
@@ -34,7 +33,7 @@
) : PropertyTransformation<IntSize> {
override fun transform(
layoutImpl: SceneTransitionLayoutImpl,
- scene: Scene,
+ scene: SceneKey,
element: Element,
sceneState: Element.SceneState,
transition: TransitionState.Transition,
@@ -60,7 +59,7 @@
// This simple implementation assumes that the size of [element] is the same as the size of
// the [anchor] in [scene], so simply transform to the size of the anchor in the other
// scene.
- return if (scene.key == transition.fromScene) {
+ return if (scene == transition.fromScene) {
anchorSizeIn(transition.toScene)
} else {
anchorSizeIn(transition.fromScene)
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
index 2bab4f8..70dca4c 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
@@ -21,7 +21,6 @@
import com.android.compose.animation.scene.Element
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.Scene
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneTransitionLayoutImpl
import com.android.compose.animation.scene.TransitionState
@@ -33,7 +32,7 @@
) : PropertyTransformation<Offset> {
override fun transform(
layoutImpl: SceneTransitionLayoutImpl,
- scene: Scene,
+ scene: SceneKey,
element: Element,
sceneState: Element.SceneState,
transition: TransitionState.Transition,
@@ -61,7 +60,7 @@
anchorOffsetIn(transition.toScene) ?: throwException(transition.toScene)
val offset = anchorToOffset - anchorFromOffset
- return if (scene.key == transition.toScene) {
+ return if (scene == transition.toScene) {
Offset(
value.x - offset.x,
value.y - offset.y,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
index 6704a3b..98c2dd3 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
@@ -20,7 +20,7 @@
import com.android.compose.animation.scene.Element
import com.android.compose.animation.scene.ElementMatcher
import com.android.compose.animation.scene.Scale
-import com.android.compose.animation.scene.Scene
+import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneTransitionLayoutImpl
import com.android.compose.animation.scene.TransitionState
@@ -37,7 +37,7 @@
override fun transform(
layoutImpl: SceneTransitionLayoutImpl,
- scene: Scene,
+ scene: SceneKey,
element: Element,
sceneState: Element.SceneState,
transition: TransitionState.Transition,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
index 191a8fb..aa8dc38 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
@@ -20,7 +20,7 @@
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.Element
import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.Scene
+import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneTransitionLayoutImpl
import com.android.compose.animation.scene.TransitionState
@@ -32,13 +32,13 @@
) : PropertyTransformation<Offset> {
override fun transform(
layoutImpl: SceneTransitionLayoutImpl,
- scene: Scene,
+ scene: SceneKey,
element: Element,
sceneState: Element.SceneState,
transition: TransitionState.Transition,
value: Offset
): Offset {
- val sceneSize = scene.targetSize
+ val sceneSize = layoutImpl.scene(scene).targetSize
val elementSize = sceneState.targetSize
if (elementSize == Element.SizeUnspecified) {
return value
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
index 41f626e..ada814e 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
@@ -18,7 +18,7 @@
import com.android.compose.animation.scene.Element
import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.Scene
+import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneTransitionLayoutImpl
import com.android.compose.animation.scene.TransitionState
@@ -28,7 +28,7 @@
) : PropertyTransformation<Float> {
override fun transform(
layoutImpl: SceneTransitionLayoutImpl,
- scene: Scene,
+ scene: SceneKey,
element: Element,
sceneState: Element.SceneState,
transition: TransitionState.Transition,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
index f5207dc..dca8f85 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
@@ -19,7 +19,7 @@
import androidx.compose.ui.unit.IntSize
import com.android.compose.animation.scene.Element
import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.Scene
+import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneTransitionLayoutImpl
import com.android.compose.animation.scene.TransitionState
import kotlin.math.roundToInt
@@ -35,7 +35,7 @@
) : PropertyTransformation<IntSize> {
override fun transform(
layoutImpl: SceneTransitionLayoutImpl,
- scene: Scene,
+ scene: SceneKey,
element: Element,
sceneState: Element.SceneState,
transition: TransitionState.Transition,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
index 603f7ba..7be9ce1 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
@@ -21,7 +21,7 @@
import androidx.compose.ui.util.fastCoerceIn
import com.android.compose.animation.scene.Element
import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.Scene
+import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneTransitionLayoutImpl
import com.android.compose.animation.scene.TransitionState
@@ -61,7 +61,7 @@
// to these internal classes.
fun transform(
layoutImpl: SceneTransitionLayoutImpl,
- scene: Scene,
+ scene: SceneKey,
element: Element,
sceneState: Element.SceneState,
transition: TransitionState.Transition,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
index 849c9d7..f066511 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
@@ -22,7 +22,7 @@
import com.android.compose.animation.scene.Element
import com.android.compose.animation.scene.ElementMatcher
import com.android.compose.animation.scene.OverscrollScope
-import com.android.compose.animation.scene.Scene
+import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneTransitionLayoutImpl
import com.android.compose.animation.scene.TransitionState
@@ -33,7 +33,7 @@
) : PropertyTransformation<Offset> {
override fun transform(
layoutImpl: SceneTransitionLayoutImpl,
- scene: Scene,
+ scene: SceneKey,
element: Element,
sceneState: Element.SceneState,
transition: TransitionState.Transition,
@@ -55,7 +55,7 @@
) : PropertyTransformation<Offset> {
override fun transform(
layoutImpl: SceneTransitionLayoutImpl,
- scene: Scene,
+ scene: SceneKey,
element: Element,
sceneState: Element.SceneState,
transition: TransitionState.Transition,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt
index 79f126d..ed98885 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt
@@ -17,6 +17,7 @@
package com.android.compose.animation.scene.transition.link
import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.TransitionKey
import com.android.compose.animation.scene.TransitionState
import kotlinx.coroutines.Job
@@ -25,6 +26,7 @@
private val originalTransition: TransitionState.Transition,
fromScene: SceneKey,
toScene: SceneKey,
+ override val key: TransitionKey? = null,
) : TransitionState.Transition(fromScene, toScene) {
override val currentScene: SceneKey
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
index 6e8b208..a7889e2 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
@@ -18,10 +18,13 @@
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.tween
+import androidx.compose.foundation.gestures.Orientation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@@ -443,4 +446,56 @@
assertThat(lastValues[bar]?.get(SceneC)).isWithin(0.001f).of(7f)
assertThat(lastValues[bar]?.get(SceneD)).isWithin(0.001f).of(7f)
}
+
+ @Test
+ fun animatedValueDoesNotOverscrollWhenOverscrollIsSpecified() {
+ val state =
+ rule.runOnUiThread {
+ MutableSceneTransitionLayoutStateImpl(
+ SceneA,
+ transitions { overscroll(SceneB, Orientation.Horizontal) }
+ )
+ }
+
+ val key = ValueKey("foo")
+ val lastValues = mutableMapOf<SceneKey, Float>()
+
+ @Composable
+ fun SceneScope.animateFloat(value: Float, key: ValueKey) {
+ val animatedValue = animateSceneFloatAsState(value, key)
+ LaunchedEffect(animatedValue) {
+ snapshotFlow { animatedValue.value }.collect { lastValues[sceneKey] = it }
+ }
+ }
+
+ rule.setContent {
+ SceneTransitionLayout(state) {
+ scene(SceneA) { animateFloat(0f, key) }
+ scene(SceneB) { animateFloat(100f, key) }
+ }
+ }
+
+ // Overscroll on A at -100%: value should be interpolated given that there is no overscroll
+ // defined for scene A.
+ var progress by mutableStateOf(-1f)
+ rule.runOnIdle {
+ state.startTransition(transition(from = SceneA, to = SceneB, progress = { progress }))
+ }
+ rule.waitForIdle()
+ assertThat(lastValues[SceneA]).isWithin(0.001f).of(-100f)
+ assertThat(lastValues[SceneB]).isWithin(0.001f).of(-100f)
+
+ // Middle of the transition.
+ progress = 0.5f
+ rule.waitForIdle()
+ assertThat(lastValues[SceneA]).isWithin(0.001f).of(50f)
+ assertThat(lastValues[SceneB]).isWithin(0.001f).of(50f)
+
+ // Overscroll on B at 200%: value should not be interpolated given that there is an
+ // overscroll defined for scene B.
+ progress = 2f
+ rule.waitForIdle()
+ assertThat(lastValues[SceneA]).isWithin(0.001f).of(100f)
+ assertThat(lastValues[SceneB]).isWithin(0.001f).of(100f)
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
index 47c9b9c..a18da73 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
@@ -52,6 +52,7 @@
import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.assertPositionInRootIsEqualTo
import androidx.compose.ui.test.assertTopPositionInRootIsEqualTo
+import androidx.compose.ui.test.hasTestTag
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
@@ -639,10 +640,7 @@
// Change the current transition.
rule.runOnUiThread {
- state.startTransition(
- transition(from = SceneA, to = SceneB, progress = { 0.5f }),
- transitionKey = null,
- )
+ state.startTransition(transition(from = SceneA, to = SceneB, progress = { 0.5f }))
}
// The size of Foo should still be 20dp given that the new state was not composed yet.
@@ -1171,7 +1169,7 @@
val offsetInAToB = lerp(offsetInA, offsetInB, aToBProgress)
val sizeInAToB = lerp(sizeInA, sizeInB, aToBProgress)
val valueInAToB = lerp(valueInA, valueInB, aToBProgress)
- rule.runOnUiThread { state.startTransition(aToB, transitionKey = null) }
+ rule.runOnUiThread { state.startTransition(aToB) }
rule
.onNode(isElement(TestElements.Foo, SceneB))
.assertSizeIsEqualTo(sizeInAToB)
@@ -1191,7 +1189,7 @@
progress = { bToCProgress },
interruptionProgress = { interruptionProgress },
)
- rule.runOnUiThread { state.startTransition(bToC, transitionKey = null) }
+ rule.runOnUiThread { state.startTransition(bToC) }
// The interruption deltas, which will be multiplied by the interruption progress then added
// to the current transition offset and size.
@@ -1333,9 +1331,9 @@
interruptionProgress = { bToCInterruptionProgress },
onFinish = neverFinish(),
)
- rule.runOnUiThread { state.startTransition(aToB, transitionKey = null) }
+ rule.runOnUiThread { state.startTransition(aToB) }
rule.waitForIdle()
- rule.runOnUiThread { state.startTransition(bToC, transitionKey = null) }
+ rule.runOnUiThread { state.startTransition(bToC) }
// Foo is placed in both B and C given that the shared transition is disabled. In B, its
// offset is impacted by the interruption but in C it is not.
@@ -1371,7 +1369,7 @@
progress = { 0.7f },
interruptionProgress = { 1f },
)
- rule.runOnUiThread { state.startTransition(bToA, transitionKey = null) }
+ rule.runOnUiThread { state.startTransition(bToA) }
// Foo should have the position it had in B right before the interruption.
rule
@@ -1395,8 +1393,7 @@
to = SceneB,
progress = { -1f },
orientation = Orientation.Horizontal
- ),
- transitionKey = null,
+ )
)
}
}
@@ -1513,8 +1510,7 @@
to = SceneB,
progress = { 0.6f },
interruptionProgress = { interruptionProgress },
- ),
- transitionKey = null
+ )
)
}
rule.waitForIdle()
@@ -1632,4 +1628,313 @@
rule.onNode(hasText(fooInA)).assertIsDisplayed()
rule.onNode(hasText(fooInB)).assertDoesNotExist()
}
+
+ @Test
+ fun interruptionThenOverscroll() = runTest {
+ val state =
+ rule.runOnUiThread {
+ MutableSceneTransitionLayoutStateImpl(
+ SceneA,
+ transitions {
+ overscroll(SceneB, Orientation.Vertical) {
+ translate(TestElements.Foo, y = 15.dp)
+ }
+ }
+ )
+ }
+
+ @Composable
+ fun SceneScope.SceneWithFoo(offset: DpOffset, modifier: Modifier = Modifier) {
+ Box(modifier.fillMaxSize()) {
+ Box(Modifier.offset(offset.x, offset.y).element(TestElements.Foo).size(100.dp))
+ }
+ }
+
+ rule.setContent {
+ SceneTransitionLayout(state, Modifier.size(200.dp)) {
+ scene(SceneA) { SceneWithFoo(offset = DpOffset.Zero) }
+ scene(SceneB) { SceneWithFoo(offset = DpOffset(x = 40.dp, y = 0.dp)) }
+ scene(SceneC) { SceneWithFoo(offset = DpOffset(x = 40.dp, y = 40.dp)) }
+ }
+ }
+
+ // Start A => B at 75%.
+ rule.runOnUiThread {
+ state.startTransition(
+ transition(
+ from = SceneA,
+ to = SceneB,
+ progress = { 0.75f },
+ onFinish = neverFinish(),
+ )
+ )
+ }
+
+ // Foo should be at offset (30dp, 0dp) and placed in scene B.
+ rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsNotDisplayed()
+ rule.onNode(isElement(TestElements.Foo, SceneB)).assertPositionInRootIsEqualTo(30.dp, 0.dp)
+ rule.onNode(isElement(TestElements.Foo, SceneC)).assertIsNotDisplayed()
+
+ // Interrupt A => B with B => C at 0%.
+ var progress by mutableStateOf(0f)
+ var interruptionProgress by mutableStateOf(1f)
+ rule.runOnUiThread {
+ state.startTransition(
+ transition(
+ from = SceneB,
+ to = SceneC,
+ progress = { progress },
+ interruptionProgress = { interruptionProgress },
+ orientation = Orientation.Vertical,
+ onFinish = neverFinish(),
+ )
+ )
+ }
+
+ // Because interruption progress is at 100M, Foo should still be at offset (30dp, 0dp) but
+ // placed in scene C.
+ rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsNotDisplayed()
+ rule.onNode(isElement(TestElements.Foo, SceneB)).assertIsNotDisplayed()
+ rule.onNode(isElement(TestElements.Foo, SceneC)).assertPositionInRootIsEqualTo(30.dp, 0.dp)
+
+ // Overscroll B => C on scene B at -100%. Because overscrolling on B => C translates Foo
+ // vertically by -15dp and that interruptionProgress is still 100%, we should now be at
+ // (30dp, -15dp)
+ progress = -1f
+ rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsNotDisplayed()
+ rule
+ .onNode(isElement(TestElements.Foo, SceneB))
+ .assertPositionInRootIsEqualTo(30.dp, -15.dp)
+ rule.onNode(isElement(TestElements.Foo, SceneC)).assertIsNotDisplayed()
+
+ // Finish the interruption, we should now be at (40dp, -15dp), still on scene B.
+ interruptionProgress = 0f
+ rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsNotDisplayed()
+ rule
+ .onNode(isElement(TestElements.Foo, SceneB))
+ .assertPositionInRootIsEqualTo(40.dp, -15.dp)
+ rule.onNode(isElement(TestElements.Foo, SceneC)).assertIsNotDisplayed()
+
+ // Finish the transition, we should be at the final position (40dp, 40dp) on scene C.
+ progress = 1f
+ rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsNotDisplayed()
+ rule.onNode(isElement(TestElements.Foo, SceneB)).assertIsNotDisplayed()
+ rule.onNode(isElement(TestElements.Foo, SceneC)).assertPositionInRootIsEqualTo(40.dp, 40.dp)
+ }
+
+ @Test
+ fun lastPlacementValuesAreClearedOnNestedElements() {
+ val state = rule.runOnIdle { MutableSceneTransitionLayoutStateImpl(SceneA) }
+
+ @Composable
+ fun SceneScope.NestedFooBar() {
+ Box(Modifier.element(TestElements.Foo)) {
+ Box(Modifier.element(TestElements.Bar).size(10.dp))
+ }
+ }
+
+ lateinit var layoutImpl: SceneTransitionLayoutImpl
+ rule.setContent {
+ SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) {
+ scene(SceneA) { NestedFooBar() }
+ scene(SceneB) { NestedFooBar() }
+ }
+ }
+
+ // Idle on A: composed and placed only in B.
+ rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsDisplayed()
+ rule.onNode(isElement(TestElements.Bar, SceneA)).assertIsDisplayed()
+ rule.onNode(isElement(TestElements.Foo, SceneB)).assertDoesNotExist()
+ rule.onNode(isElement(TestElements.Bar, SceneB)).assertDoesNotExist()
+
+ assertThat(layoutImpl.elements).containsKey(TestElements.Foo)
+ assertThat(layoutImpl.elements).containsKey(TestElements.Bar)
+ val foo = layoutImpl.elements.getValue(TestElements.Foo)
+ val bar = layoutImpl.elements.getValue(TestElements.Bar)
+
+ assertThat(foo.sceneStates).containsKey(SceneA)
+ assertThat(bar.sceneStates).containsKey(SceneA)
+ assertThat(foo.sceneStates).doesNotContainKey(SceneB)
+ assertThat(bar.sceneStates).doesNotContainKey(SceneB)
+
+ val fooInA = foo.sceneStates.getValue(SceneA)
+ val barInA = bar.sceneStates.getValue(SceneA)
+ assertThat(fooInA.lastOffset).isNotEqualTo(Offset.Unspecified)
+ assertThat(fooInA.lastAlpha).isNotEqualTo(Element.AlphaUnspecified)
+ assertThat(fooInA.lastScale).isNotEqualTo(Scale.Unspecified)
+
+ assertThat(barInA.lastOffset).isNotEqualTo(Offset.Unspecified)
+ assertThat(barInA.lastAlpha).isNotEqualTo(Element.AlphaUnspecified)
+ assertThat(barInA.lastScale).isNotEqualTo(Scale.Unspecified)
+
+ // A => B: composed in both and placed only in B.
+ rule.runOnUiThread { state.startTransition(transition(from = SceneA, to = SceneB)) }
+ rule.onNode(isElement(TestElements.Foo, SceneA)).assertExists().assertIsNotDisplayed()
+ rule.onNode(isElement(TestElements.Bar, SceneA)).assertExists().assertIsNotDisplayed()
+ rule.onNode(isElement(TestElements.Foo, SceneB)).assertIsDisplayed()
+ rule.onNode(isElement(TestElements.Bar, SceneB)).assertIsDisplayed()
+
+ assertThat(foo.sceneStates).containsKey(SceneB)
+ assertThat(bar.sceneStates).containsKey(SceneB)
+
+ val fooInB = foo.sceneStates.getValue(SceneB)
+ val barInB = bar.sceneStates.getValue(SceneB)
+ assertThat(fooInA.lastOffset).isEqualTo(Offset.Unspecified)
+ assertThat(fooInA.lastAlpha).isEqualTo(Element.AlphaUnspecified)
+ assertThat(fooInA.lastScale).isEqualTo(Scale.Unspecified)
+ assertThat(fooInB.lastOffset).isNotEqualTo(Offset.Unspecified)
+ assertThat(fooInB.lastAlpha).isNotEqualTo(Element.AlphaUnspecified)
+ assertThat(fooInB.lastScale).isNotEqualTo(Scale.Unspecified)
+
+ assertThat(barInA.lastOffset).isEqualTo(Offset.Unspecified)
+ assertThat(barInA.lastAlpha).isEqualTo(Element.AlphaUnspecified)
+ assertThat(barInA.lastScale).isEqualTo(Scale.Unspecified)
+ assertThat(barInB.lastOffset).isNotEqualTo(Offset.Unspecified)
+ assertThat(barInB.lastAlpha).isNotEqualTo(Element.AlphaUnspecified)
+ assertThat(barInB.lastScale).isNotEqualTo(Scale.Unspecified)
+ }
+
+ @Test
+ fun currentTransitionSceneIsUsedToComputeElementValues() = runTest {
+ val state =
+ rule.runOnIdle {
+ MutableSceneTransitionLayoutStateImpl(
+ SceneA,
+ transitions {
+ from(SceneB, to = SceneC) {
+ scaleSize(TestElements.Foo, width = 2f, height = 3f)
+ }
+ }
+ )
+ }
+
+ @Composable
+ fun SceneScope.Foo() {
+ Box(Modifier.testTag("fooParentIn${sceneKey.debugName}")) {
+ Box(Modifier.element(TestElements.Foo).size(20.dp))
+ }
+ }
+
+ rule.setContent {
+ SceneTransitionLayout(state, Modifier.size(200.dp)) {
+ scene(SceneA) { Foo() }
+ scene(SceneB) {}
+ scene(SceneC) { Foo() }
+ }
+ }
+
+ // We have 2 transitions:
+ // - A => B at 100%
+ // - B => C at 0%
+ // So Foo should have a size of (40dp, 60dp) in both A and C given that it is scaling its
+ // size in B => C.
+ rule.runOnUiThread {
+ state.startTransition(
+ transition(from = SceneA, to = SceneB, progress = { 1f }, onFinish = neverFinish())
+ )
+ state.startTransition(transition(from = SceneB, to = SceneC, progress = { 0f }))
+ }
+
+ rule.onNode(hasTestTag("fooParentInSceneA")).assertSizeIsEqualTo(40.dp, 60.dp)
+ rule.onNode(hasTestTag("fooParentInSceneC")).assertSizeIsEqualTo(40.dp, 60.dp)
+ }
+
+ @Test
+ fun interruptionDeltasAreProperlyCleaned() = runTest {
+ val state = rule.runOnIdle { MutableSceneTransitionLayoutStateImpl(SceneA) }
+
+ @Composable
+ fun SceneScope.Foo(offset: Dp) {
+ Box(Modifier.fillMaxSize()) {
+ Box(Modifier.offset(offset, offset).element(TestElements.Foo).size(20.dp))
+ }
+ }
+
+ rule.setContent {
+ SceneTransitionLayout(state, Modifier.size(200.dp)) {
+ scene(SceneA) { Foo(offset = 0.dp) }
+ scene(SceneB) { Foo(offset = 20.dp) }
+ scene(SceneC) { Foo(offset = 40.dp) }
+ }
+ }
+
+ // Start A => B at 50%.
+ val aToB =
+ transition(from = SceneA, to = SceneB, progress = { 0.5f }, onFinish = neverFinish())
+ rule.runOnUiThread { state.startTransition(aToB) }
+ rule.onNode(isElement(TestElements.Foo, SceneB)).assertPositionInRootIsEqualTo(10.dp, 10.dp)
+
+ // Start B => C at 0%. This will compute an interruption delta of (-10dp, -10dp) so that the
+ // position of Foo is unchanged and converges to (20dp, 20dp).
+ var interruptionProgress by mutableStateOf(1f)
+ val bToC =
+ transition(
+ from = SceneB,
+ to = SceneC,
+ progress = { 0f },
+ interruptionProgress = { interruptionProgress },
+ onFinish = neverFinish(),
+ )
+ rule.runOnUiThread { state.startTransition(bToC) }
+ rule.onNode(isElement(TestElements.Foo, SceneC)).assertPositionInRootIsEqualTo(10.dp, 10.dp)
+
+ // Finish the interruption and leave the transition progress at 0f. We should be at the same
+ // state as in B.
+ interruptionProgress = 0f
+ rule.onNode(isElement(TestElements.Foo, SceneC)).assertPositionInRootIsEqualTo(20.dp, 20.dp)
+
+ // Finish both transitions but directly start a new one B => A with interruption progress
+ // 100%. We should be at (20dp, 20dp), unless the interruption deltas have not been
+ // correctly cleaned.
+ rule.runOnUiThread {
+ state.finishTransition(aToB, idleScene = SceneB)
+ state.finishTransition(bToC, idleScene = SceneB)
+ state.startTransition(
+ transition(
+ from = SceneB,
+ to = SceneA,
+ progress = { 0f },
+ interruptionProgress = { 1f },
+ )
+ )
+ }
+ rule.onNode(isElement(TestElements.Foo, SceneB)).assertPositionInRootIsEqualTo(20.dp, 20.dp)
+ }
+
+ @Test
+ fun lastSizeIsUnspecifiedWhenOverscrollingOtherScene() = runTest {
+ val state =
+ rule.runOnIdle {
+ MutableSceneTransitionLayoutStateImpl(
+ SceneA,
+ transitions { overscroll(SceneA, Orientation.Horizontal) }
+ )
+ }
+
+ @Composable
+ fun SceneScope.Foo() {
+ Box(Modifier.element(TestElements.Foo).size(10.dp))
+ }
+
+ lateinit var layoutImpl: SceneTransitionLayoutImpl
+ rule.setContent {
+ SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) {
+ scene(SceneA) { Foo() }
+ scene(SceneB) { Foo() }
+ }
+ }
+
+ // Overscroll A => B on A.
+ rule.runOnUiThread {
+ state.startTransition(
+ transition(from = SceneA, to = SceneB, progress = { -1f }, onFinish = neverFinish())
+ )
+ }
+ rule.waitForIdle()
+
+ assertThat(
+ layoutImpl.elements.getValue(TestElements.Foo).sceneStates.getValue(SceneB).lastSize
+ )
+ .isEqualTo(Element.SizeUnspecified)
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt
index 85d4165..09d1a82 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt
@@ -40,7 +40,7 @@
val state =
MutableSceneTransitionLayoutState(
SceneA,
- transitions { /* default interruption handler */},
+ transitions { /* default interruption handler */ },
)
state.setTargetScene(SceneB, coroutineScope = this)
@@ -160,7 +160,7 @@
progressVelocity = { progressVelocity },
onFinish = { launch {} },
)
- state.startTransition(aToB, transitionKey = null)
+ state.startTransition(aToB)
// Animate back to A. The previous transition is reversed, i.e. it has the same (from, to)
// pair, and its velocity is used when animating the progress back to 0.
@@ -186,7 +186,7 @@
progressVelocity = { progressVelocity },
onFinish = { launch {} },
)
- state.startTransition(aToB, transitionKey = null)
+ state.startTransition(aToB)
// Animate to B. The previous transition is reversed, i.e. it has the same (from, to) pair,
// and its velocity is used when animating the progress to 1.
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
index 4bb643f..1a0740b 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
@@ -349,6 +349,121 @@
}
@Test
+ fun multiPointerDuringAnotherGestureWaitAConsumableEventAfterMainPass() {
+ val size = 200f
+ val middle = Offset(size / 2f, size / 2f)
+
+ var verticalStarted = false
+ var verticalDragged = false
+ var verticalStopped = false
+ var horizontalStarted = false
+ var horizontalDragged = false
+ var horizontalStopped = false
+
+ var touchSlop = 0f
+ rule.setContent {
+ touchSlop = LocalViewConfiguration.current.touchSlop
+ Box(
+ Modifier.size(with(LocalDensity.current) { Size(size, size).toDpSize() })
+ .multiPointerDraggable(
+ orientation = Orientation.Vertical,
+ enabled = { true },
+ startDragImmediately = { false },
+ onDragStarted = { _, _, _ ->
+ verticalStarted = true
+ object : DragController {
+ override fun onDrag(delta: Float) {
+ verticalDragged = true
+ }
+
+ override fun onStop(velocity: Float, canChangeScene: Boolean) {
+ verticalStopped = true
+ }
+ }
+ },
+ )
+ .multiPointerDraggable(
+ orientation = Orientation.Horizontal,
+ enabled = { true },
+ startDragImmediately = { false },
+ onDragStarted = { _, _, _ ->
+ horizontalStarted = true
+ object : DragController {
+ override fun onDrag(delta: Float) {
+ horizontalDragged = true
+ }
+
+ override fun onStop(velocity: Float, canChangeScene: Boolean) {
+ horizontalStopped = true
+ }
+ }
+ },
+ )
+ )
+ }
+
+ fun startDraggingDown() {
+ rule.onRoot().performTouchInput {
+ down(middle)
+ moveBy(Offset(0f, touchSlop))
+ }
+ }
+
+ fun startDraggingRight() {
+ rule.onRoot().performTouchInput {
+ down(middle)
+ moveBy(Offset(touchSlop, 0f))
+ }
+ }
+
+ fun stopDragging() {
+ rule.onRoot().performTouchInput { up() }
+ }
+
+ fun continueDown() {
+ rule.onRoot().performTouchInput { moveBy(Offset(0f, touchSlop)) }
+ }
+
+ fun continueRight() {
+ rule.onRoot().performTouchInput { moveBy(Offset(touchSlop, 0f)) }
+ }
+
+ startDraggingDown()
+ assertThat(verticalStarted).isTrue()
+ assertThat(verticalDragged).isTrue()
+ assertThat(verticalStopped).isFalse()
+
+ // Ignore right swipe, do not interrupt the dragging gesture.
+ continueRight()
+ assertThat(horizontalStarted).isFalse()
+ assertThat(horizontalDragged).isFalse()
+ assertThat(horizontalStopped).isFalse()
+ assertThat(verticalStopped).isFalse()
+
+ stopDragging()
+ assertThat(verticalStopped).isTrue()
+
+ verticalStarted = false
+ verticalDragged = false
+ verticalStopped = false
+
+ startDraggingRight()
+ assertThat(horizontalStarted).isTrue()
+ assertThat(horizontalDragged).isTrue()
+ assertThat(horizontalStopped).isFalse()
+
+ // Ignore down swipe, do not interrupt the dragging gesture.
+ continueDown()
+ assertThat(verticalStarted).isFalse()
+ assertThat(verticalDragged).isFalse()
+ assertThat(verticalStopped).isFalse()
+ assertThat(horizontalStopped).isFalse()
+
+ stopDragging()
+ assertThat(horizontalStopped).isTrue()
+ }
+
+ @Test
fun multiPointerSwipeDetectorInteraction() {
val size = 200f
val middle = Offset(size / 2f, size / 2f)
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt
index 2a75e13..5543135 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt
@@ -135,7 +135,7 @@
var transitionCurrentScene by mutableStateOf(SceneA)
val transition =
transition(from = SceneA, to = SceneB, current = { transitionCurrentScene })
- state.startTransition(transition, transitionKey = null)
+ state.startTransition(transition)
assertThat(currentScene.value).isEqualTo(SceneA)
// Change the transition current scene.
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
index d2c8bd6..de6f1cc 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
@@ -57,7 +57,7 @@
@Test
fun isTransitioningTo_transition() {
val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)
- state.startTransition(transition(from = SceneA, to = SceneB), transitionKey = null)
+ state.startTransition(transition(from = SceneA, to = SceneB))
assertThat(state.isTransitioning()).isTrue()
assertThat(state.isTransitioning(from = SceneA)).isTrue()
@@ -175,7 +175,7 @@
val childTransition = transition(SceneA, SceneB)
- childState.startTransition(childTransition, null)
+ childState.startTransition(childTransition)
assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue()
@@ -211,7 +211,7 @@
val childTransition = transition(SceneA, SceneB)
- childState.startTransition(childTransition, null)
+ childState.startTransition(childTransition)
assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue()
assertThat(parentParentState.isTransitioning(SceneB, SceneC)).isTrue()
@@ -229,7 +229,7 @@
var progress = 0f
val childTransition = transition(SceneA, SceneB, progress = { progress })
- childState.startTransition(childTransition, null)
+ childState.startTransition(childTransition)
assertThat(parentState.currentTransition?.progress).isEqualTo(0f)
progress = .5f
@@ -242,7 +242,7 @@
val childTransition = transition(SceneB, SceneA)
- childState.startTransition(childTransition, null)
+ childState.startTransition(childTransition)
assertThat(childState.isTransitioning(SceneB, SceneA)).isTrue()
assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneC))
@@ -256,7 +256,7 @@
val (parentState, childState) = setupLinkedStates()
val childTransition = transition(SceneA, SceneB)
- childState.startTransition(childTransition, null)
+ childState.startTransition(childTransition)
childState.finishTransition(childTransition, SceneA)
assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneA))
@@ -268,7 +268,7 @@
val (parentState, childState) = setupLinkedStates()
val childTransition = transition(SceneA, SceneB)
- childState.startTransition(childTransition, null)
+ childState.startTransition(childTransition)
childState.finishTransition(childTransition, SceneD)
assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneD))
@@ -283,16 +283,16 @@
transition(
SceneA,
SceneB,
- onFinish = { launch { /* Do nothing. */} },
+ onFinish = { launch { /* Do nothing. */ } },
)
val parentTransition =
transition(
SceneC,
SceneA,
- onFinish = { launch { /* Do nothing. */} },
+ onFinish = { launch { /* Do nothing. */ } },
)
- childState.startTransition(childTransition, null)
- parentState.startTransition(parentTransition, null)
+ childState.startTransition(childTransition)
+ parentState.startTransition(parentTransition)
childState.finishTransition(childTransition, SceneB)
assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB))
@@ -341,10 +341,7 @@
@Test
fun snapToIdleIfClose_snapToStart() = runMonotonicClockTest {
val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)
- state.startTransition(
- transition(from = SceneA, to = SceneB, progress = { 0.2f }),
- transitionKey = null
- )
+ state.startTransition(transition(from = SceneA, to = SceneB, progress = { 0.2f }))
assertThat(state.isTransitioning()).isTrue()
// Ignore the request if the progress is not close to 0 or 1, using the threshold.
@@ -360,10 +357,7 @@
@Test
fun snapToIdleIfClose_snapToEnd() = runMonotonicClockTest {
val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)
- state.startTransition(
- transition(from = SceneA, to = SceneB, progress = { 0.8f }),
- transitionKey = null
- )
+ state.startTransition(transition(from = SceneA, to = SceneB, progress = { 0.8f }))
assertThat(state.isTransitioning()).isTrue()
// Ignore the request if the progress is not close to 0 or 1, using the threshold.
@@ -385,13 +379,13 @@
from = SceneA,
to = SceneB,
progress = { 0.5f },
- onFinish = { launch { /* do nothing */} },
+ onFinish = { launch { /* do nothing */ } },
)
- state.startTransition(aToB, transitionKey = null)
+ state.startTransition(aToB)
assertThat(state.currentTransitions).containsExactly(aToB).inOrder()
val bToC = transition(from = SceneB, to = SceneC, progress = { 0.8f })
- state.startTransition(bToC, transitionKey = null)
+ state.startTransition(bToC)
assertThat(state.currentTransitions).containsExactly(aToB, bToC).inOrder()
// Ignore the request if the progress is not close to 0 or 1, using the threshold.
@@ -409,7 +403,7 @@
val (parentState, childState) = setupLinkedStates(SceneC, SceneA, null, null, null, SceneD)
val childTransition = transition(SceneA, SceneB)
- childState.startTransition(childTransition, null)
+ childState.startTransition(childTransition)
assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue()
@@ -425,7 +419,7 @@
val childTransition = transition(SceneA, SceneB)
- childState.startTransition(childTransition, null)
+ childState.startTransition(childTransition)
assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue()
@@ -440,7 +434,7 @@
setupLinkedStates(SceneC, SceneA, SceneB, null, SceneC, SceneD)
val childTransition = transition(SceneA, SceneB)
- childState.startTransition(childTransition, null)
+ childState.startTransition(childTransition)
assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
assertThat(parentState.isTransitioning(SceneC, SceneD)).isFalse()
}
@@ -460,8 +454,7 @@
to = SceneB,
progress = progress,
orientation = Orientation.Vertical,
- ),
- transitionKey = null
+ )
)
assertThat(state.isTransitioning()).isTrue()
return state
@@ -583,19 +576,19 @@
assertThat(state.currentTransitions).isEmpty()
// A => B.
- state.startTransition(aToB, transitionKey = null)
+ state.startTransition(aToB)
assertThat(finishingTransitions).isEmpty()
assertThat(state.finishedTransitions).isEmpty()
assertThat(state.currentTransitions).containsExactly(aToB).inOrder()
// B => C. This should automatically call finish() on aToB.
- state.startTransition(bToC, transitionKey = null)
+ state.startTransition(bToC)
assertThat(finishingTransitions).containsExactly(aToB)
assertThat(state.finishedTransitions).isEmpty()
assertThat(state.currentTransitions).containsExactly(aToB, bToC).inOrder()
// C => A. This should automatically call finish() on bToC.
- state.startTransition(cToA, transitionKey = null)
+ state.startTransition(cToA)
assertThat(finishingTransitions).containsExactly(aToB, bToC)
assertThat(state.finishedTransitions).isEmpty()
assertThat(state.currentTransitions).containsExactly(aToB, bToC, cToA).inOrder()
@@ -617,8 +610,8 @@
val state = MutableSceneTransitionLayoutStateImpl(SceneA, EmptyTestTransitions)
fun startTransition() {
- val transition = transition(SceneA, SceneB, onFinish = { launch { /* do nothing */} })
- state.startTransition(transition, transitionKey = null)
+ val transition = transition(SceneA, SceneB, onFinish = { launch { /* do nothing */ } })
+ state.startTransition(transition)
}
var hasLoggedWtf = false
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
index 08532bd..a8dd572 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
@@ -21,6 +21,7 @@
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
+import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
@@ -333,6 +334,42 @@
}
@Test
+ fun layoutSizeDoesNotOverscrollWhenOverscrollIsSpecified() {
+ val state =
+ rule.runOnUiThread {
+ MutableSceneTransitionLayoutStateImpl(
+ SceneA,
+ transitions { overscroll(SceneB, Orientation.Horizontal) }
+ )
+ }
+
+ val layoutTag = "layout"
+ rule.setContent {
+ SceneTransitionLayout(state, Modifier.testTag(layoutTag)) {
+ scene(SceneA) { Box(Modifier.size(50.dp)) }
+ scene(SceneB) { Box(Modifier.size(70.dp)) }
+ }
+ }
+
+ // Overscroll on A at -100%: size should be interpolated given that there is no overscroll
+ // defined for scene A.
+ var progress by mutableStateOf(-1f)
+ rule.runOnIdle {
+ state.startTransition(transition(from = SceneA, to = SceneB, progress = { progress }))
+ }
+ rule.onNodeWithTag(layoutTag).assertSizeIsEqualTo(30.dp)
+
+ // Middle of the transition.
+ progress = 0.5f
+ rule.onNodeWithTag(layoutTag).assertSizeIsEqualTo(60.dp)
+
+ // Overscroll on B at 200%: size should not be interpolated given that there is an
+ // overscroll defined for scene B.
+ progress = 2f
+ rule.onNodeWithTag(layoutTag).assertSizeIsEqualTo(70.dp)
+ }
+
+ @Test
fun multipleTransitionsWillComposeMultipleScenes() {
val duration = 10 * 16L
diff --git a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt
index e743c78..6d063a0 100644
--- a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt
+++ b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt
@@ -17,7 +17,7 @@
package com.android.compose.animation.scene
import androidx.compose.ui.test.SemanticsMatcher
-import androidx.compose.ui.test.hasParent
+import androidx.compose.ui.test.hasAnyAncestor
import androidx.compose.ui.test.hasTestTag
/** A [SemanticsMatcher] that matches [element], optionally restricted to scene [scene]. */
@@ -25,6 +25,6 @@
return if (scene == null) {
hasTestTag(element.testTag)
} else {
- hasTestTag(element.testTag) and hasParent(hasTestTag(scene.testTag))
+ hasTestTag(element.testTag) and hasAnyAncestor(hasTestTag(scene.testTag))
}
}
diff --git a/packages/SystemUI/flag_check.py b/packages/SystemUI/flag_check.py
index 95a25c5..d78ef5a 100755
--- a/packages/SystemUI/flag_check.py
+++ b/packages/SystemUI/flag_check.py
@@ -52,7 +52,7 @@
nargs='?',
default='',
help=
- 'REPO_PATH in repo upload to determine whether the check should run for this project.')
+ 'REPO_PROJECT in repo upload to determine whether the check should run for this project.')
# Parse the arguments
args = parser.parse_args()
@@ -112,16 +112,16 @@
sys.exit(0)
-def should_run_path(path, files):
+def should_run_path(project, files):
"""Returns a boolean if this check should run with these paths.
If you want to check for a particular subdirectory under the path,
add a check here, call should_run_files and check for a specific sub dir path in should_run_files.
"""
- if not path:
+ if not project:
return False
- if path == 'frameworks/base':
+ if project == 'platform/frameworks/base':
return should_run_files(files)
- # Default case, run for all other paths which calls this script.
+ # Default case, run for all other projects which calls this script.
return True
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewControllerTest.java
similarity index 87%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewControllerTest.java
index f561c53..201ed00 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewControllerTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.dreams;
+package com.android.systemui.ambient.statusbar.ui;
import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
@@ -44,6 +44,10 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dreams.DreamOverlayNotificationCountProvider;
+import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.dreams.DreamOverlayStatusBarItemsProvider;
+import com.android.systemui.kosmos.KosmosJavaAdapter;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.core.FakeLogBuffer;
import com.android.systemui.res.R;
@@ -54,7 +58,6 @@
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.statusbar.window.StatusBarWindowStateListener;
-import com.android.systemui.touch.TouchInsetManager;
import com.android.systemui.util.time.DateFormatUtil;
import org.junit.Before;
@@ -72,14 +75,12 @@
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@RunWith(AndroidJUnit4.class)
-public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
+public class AmbientStatusBarViewControllerTest extends SysuiTestCase {
private static final String NOTIFICATION_INDICATOR_FORMATTER_STRING =
"{count, plural, =1 {# notification} other {# notifications}}";
@Mock
- MockDreamOverlayStatusBarView mView;
- @Mock
- TouchInsetManager.TouchInsetSession mTouchSession;
+ MockAmbientStatusBarView mView;
@Mock
Resources mResources;
@Mock
@@ -114,9 +115,11 @@
private final Executor mMainExecutor = Runnable::run;
- private final FakeWifiRepository mWifiRepository = new FakeWifiRepository();
+ private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
- DreamOverlayStatusBarViewController mController;
+ private final FakeWifiRepository mWifiRepository = mKosmos.getFakeWifiRepository();
+
+ AmbientStatusBarViewController mController;
@Before
public void setup() {
@@ -128,11 +131,10 @@
doCallRealMethod().when(mView).getVisibility();
when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser());
- mController = new DreamOverlayStatusBarViewController(
+ mController = new AmbientStatusBarViewController(
mView,
mResources,
mMainExecutor,
- mTouchSession,
mAlarmManager,
mNextAlarmController,
mDateFormatUtil,
@@ -143,7 +145,8 @@
mDreamOverlayStatusBarItemsProvider,
mDreamOverlayStateController,
mUserTracker,
- mWifiRepository,
+ mKosmos.getWifiInteractor(),
+ mKosmos.getCommunalSceneInteractor(),
mLogBuffer);
}
@@ -164,7 +167,7 @@
mController.updateWifiUnavailableStatusIcon(false);
verify(mView).showIcon(
- DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, true, null);
+ AmbientStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, true, null);
}
@Test
@@ -173,7 +176,7 @@
mController.updateWifiUnavailableStatusIcon(true);
verify(mView).showIcon(
- DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false, null);
+ AmbientStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false, null);
}
@Test
@@ -183,7 +186,7 @@
when(mAlarmManager.getNextAlarmClock(anyInt())).thenReturn(alarmClockInfo);
mController.onViewAttached();
verify(mView).showIcon(
- eq(DreamOverlayStatusBarView.STATUS_ICON_ALARM_SET), eq(true), any());
+ eq(AmbientStatusBarView.STATUS_ICON_ALARM_SET), eq(true), any());
}
@Test
@@ -191,7 +194,7 @@
when(mAlarmManager.getNextAlarmClock(anyInt())).thenReturn(null);
mController.onViewAttached();
verify(mView).showIcon(
- eq(DreamOverlayStatusBarView.STATUS_ICON_ALARM_SET), eq(false), isNull());
+ eq(AmbientStatusBarView.STATUS_ICON_ALARM_SET), eq(false), isNull());
}
@Test
@@ -202,7 +205,7 @@
.thenReturn(false);
mController.onViewAttached();
verify(mView).showIcon(
- DreamOverlayStatusBarView.STATUS_ICON_MIC_DISABLED, true, null);
+ AmbientStatusBarView.STATUS_ICON_MIC_DISABLED, true, null);
}
@Test
@@ -213,7 +216,7 @@
.thenReturn(true);
mController.onViewAttached();
verify(mView).showIcon(
- DreamOverlayStatusBarView.STATUS_ICON_CAMERA_DISABLED, true, null);
+ AmbientStatusBarView.STATUS_ICON_CAMERA_DISABLED, true, null);
}
@Test
@@ -224,7 +227,7 @@
.thenReturn(true);
mController.onViewAttached();
verify(mView).showIcon(
- DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true, null);
+ AmbientStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true, null);
}
@Test
@@ -237,7 +240,7 @@
callbackCapture.getValue().onNotificationCountChanged(1);
verify(mView).showIcon(
- eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any());
+ eq(AmbientStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any());
}
@Test
@@ -250,16 +253,15 @@
callbackCapture.getValue().onNotificationCountChanged(0);
verify(mView).showIcon(
- eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(false), isNull());
+ eq(AmbientStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(false), isNull());
}
@Test
public void testNotificationsIconNotShownWhenCountProviderAbsent() {
- DreamOverlayStatusBarViewController controller = new DreamOverlayStatusBarViewController(
+ AmbientStatusBarViewController controller = new AmbientStatusBarViewController(
mView,
mResources,
mMainExecutor,
- mTouchSession,
mAlarmManager,
mNextAlarmController,
mDateFormatUtil,
@@ -270,11 +272,12 @@
mDreamOverlayStatusBarItemsProvider,
mDreamOverlayStateController,
mUserTracker,
- mWifiRepository,
+ mKosmos.getWifiInteractor(),
+ mKosmos.getCommunalSceneInteractor(),
mLogBuffer);
controller.onViewAttached();
verify(mView, never()).showIcon(
- eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any());
+ eq(AmbientStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any());
}
@Test
@@ -283,7 +286,7 @@
Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
mController.onViewAttached();
verify(mView).showIcon(
- DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true, null);
+ AmbientStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true, null);
}
@Test
@@ -292,7 +295,7 @@
Settings.Global.ZEN_MODE_OFF);
mController.onViewAttached();
verify(mView).showIcon(
- DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false, null);
+ AmbientStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false, null);
}
@Test
@@ -322,7 +325,7 @@
callbackCapture.getValue().onNotificationCountChanged(1);
verify(mView).showIcon(
- eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any());
+ eq(AmbientStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any());
}
@Test
@@ -335,7 +338,7 @@
callbackCapture.getValue().onNotificationCountChanged(0);
verify(mView).showIcon(
- eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(false), any());
+ eq(AmbientStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(false), any());
}
@Test
@@ -354,7 +357,7 @@
SensorPrivacyManager.Sensors.MICROPHONE, true);
verify(mView).showIcon(
- DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true, null);
+ AmbientStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true, null);
}
@Test
@@ -369,7 +372,7 @@
callbackCapture.getValue().onZenChanged(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS);
verify(mView).showIcon(
- DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true, null);
+ AmbientStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true, null);
}
@Test
@@ -384,7 +387,7 @@
callbackCapture.getValue().onZenChanged(Settings.Global.ZEN_MODE_OFF);
verify(mView).showIcon(
- DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false, null);
+ AmbientStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false, null);
}
@Test
@@ -399,7 +402,7 @@
callbackCapture.getValue().onStateChanged();
verify(mView).showIcon(
- DreamOverlayStatusBarView.STATUS_ICON_ASSISTANT_ATTENTION_ACTIVE, true, null);
+ AmbientStatusBarView.STATUS_ICON_ASSISTANT_ATTENTION_ACTIVE, true, null);
}
@Test
@@ -460,7 +463,7 @@
final ArgumentCaptor<DreamOverlayStatusBarItemsProvider.Callback>
callbackCapture = ArgumentCaptor.forClass(
- DreamOverlayStatusBarItemsProvider.Callback.class);
+ DreamOverlayStatusBarItemsProvider.Callback.class);
verify(mDreamOverlayStatusBarItemsProvider).addCallback(callbackCapture.capture());
callbackCapture.getValue().onStatusBarItemsChanged(List.of(mStatusBarItem));
@@ -532,10 +535,10 @@
callback.onStateChanged();
}
- private static class MockDreamOverlayStatusBarView extends DreamOverlayStatusBarView {
+ private static class MockAmbientStatusBarView extends AmbientStatusBarView {
private int mVisibility = View.VISIBLE;
- private MockDreamOverlayStatusBarView(Context context) {
+ private MockAmbientStatusBarView(Context context) {
super(context);
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java
index 04b930e..07d8890 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java
@@ -25,12 +25,15 @@
import static org.mockito.Mockito.when;
import android.app.DreamManager;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.view.GestureDetector;
import android.view.MotionEvent;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.shared.system.InputChannelCompat;
@@ -87,7 +90,7 @@
assertThat(captured).isTrue();
}
- // Verifies that a swipe in the upward direction is not catpured.
+ // Verifies that a swipe in the upward direction is not captured.
@Test
public void testSwipeUp_notCaptured() {
final boolean captured = swipe(Direction.UP);
@@ -98,34 +101,58 @@
// Verifies that a swipe down forwards captured touches to central surfaces for handling.
@Test
- public void testSwipeDown_sentToCentralSurfaces() {
+ @EnableFlags(Flags.FLAG_COMMUNAL_HUB)
+ public void testSwipeDown_communalEnabled_sentToCentralSurfaces() {
swipe(Direction.DOWN);
- // Both motion events are sent for the shade window to process.
+ // Both motion events are sent for central surfaces to process.
verify(mCentralSurfaces, times(2)).handleExternalShadeWindowTouch(any());
}
- // Verifies that a swipe down forwards captured touches to central surfaces for handling.
+ // Verifies that a swipe down forwards captured touches to the shade view for handling.
+ @Test
+ @DisableFlags(Flags.FLAG_COMMUNAL_HUB)
+ public void testSwipeDown_communalDisabled_sentToShadeView() {
+ swipe(Direction.DOWN);
+
+ // Both motion events are sent for the shade view to process.
+ verify(mShadeViewController, times(2)).handleExternalTouch(any());
+ }
+
+ // Verifies that a swipe down while dreaming forwards captured touches to the shade view for
+ // handling.
@Test
public void testSwipeDown_dreaming_sentToShadeView() {
when(mDreamManager.isDreaming()).thenReturn(true);
swipe(Direction.DOWN);
- // Both motion events are sent for the shade window to process.
+ // Both motion events are sent for the shade view to process.
verify(mShadeViewController, times(2)).handleExternalTouch(any());
}
- // Verifies that a swipe down is not forwarded to the shade window.
+ // Verifies that a swipe up is not forwarded to central surfaces.
@Test
- public void testSwipeUp_touchesNotSent() {
+ @EnableFlags(Flags.FLAG_COMMUNAL_HUB)
+ public void testSwipeUp_communalEnabled_touchesNotSent() {
swipe(Direction.UP);
- // Motion events are not sent for the shade window to process as the swipe is going in the
+ // Motion events are not sent for central surfaces to process as the swipe is going in the
// wrong direction.
verify(mCentralSurfaces, never()).handleExternalShadeWindowTouch(any());
}
+ // Verifies that a swipe up is not forwarded to the shade view.
+ @Test
+ @DisableFlags(Flags.FLAG_COMMUNAL_HUB)
+ public void testSwipeUp_communalDisabled_touchesNotSent() {
+ swipe(Direction.UP);
+
+ // Motion events are not sent for the shade view to process as the swipe is going in the
+ // wrong direction.
+ verify(mShadeViewController, never()).handleExternalTouch(any());
+ }
+
/**
* Simulates a swipe in the given direction and returns true if the touch was intercepted by the
* touch handler's gesture listener.
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
index 9c2791f..75a77cf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
@@ -131,8 +131,9 @@
negativeButton: String = "neg",
): PromptInfo {
val info = PromptInfo()
- info.logoRes = logoRes
- info.logoBitmap = logoBitmap
+ if (logoBitmap != null) {
+ info.setLogo(logoRes, logoBitmap)
+ }
info.logoDescription = logoDescription
info.title = title
info.subtitle = subtitle
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
index ab55125..29a6e56 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
@@ -235,6 +235,7 @@
job.cancel()
}
+
@Test
fun fadeFromDialogSuggestedAlpha() =
testScope.runTest {
@@ -511,9 +512,10 @@
testScope.runTest {
// GIVEN view is attached
mController.onViewAttached()
+ val job = mController.listenForLockscreenAodTransitions(this)
+ runCurrent()
Mockito.reset(mView)
- val job = mController.listenForLockscreenAodTransitions(this)
// WHEN aod to lockscreen transition is cancelled
transitionRepository.sendTransitionStep(
TransitionStep(
@@ -537,7 +539,7 @@
// THEN doze amount is updated to zero
verify(mView)
- .onDozeAmountChanged(eq(0f), eq(0f), eq(UdfpsKeyguardViewLegacy.ANIMATION_NONE))
+ .onDozeAmountChanged(eq(0f), eq(0f), eq(ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN))
job.cancel()
}
@@ -546,9 +548,10 @@
testScope.runTest {
// GIVEN view is attached
mController.onViewAttached()
+ val job = mController.listenForLockscreenAodTransitions(this)
+ runCurrent()
Mockito.reset(mView)
- val job = mController.listenForLockscreenAodTransitions(this)
// WHEN lockscreen to aod transition is cancelled
transitionRepository.sendTransitionStep(
TransitionStep(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt
index bfed33c..fe683e07 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt
@@ -22,7 +22,6 @@
import androidx.test.filters.SmallTest
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
-import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.keyguardRepository
@@ -66,7 +65,6 @@
powerInteractor = kosmos.powerInteractor,
keyguardInteractor = kosmos.keyguardInteractor,
keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor,
- communalInteractor = kosmos.communalInteractor,
dreamManager = dreamManager,
bgScope = kosmos.applicationCoroutineScope,
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
index e61b2d0..cf14547 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
@@ -111,6 +111,25 @@
}
}
+ @Test
+ fun keyguardGoesAway_whenInEditMode_doesNotChangeScene() =
+ with(kosmos) {
+ testScope.runTest {
+ val scene by collectLastValue(communalSceneInteractor.currentScene)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal)
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
+
+ communalInteractor.setEditModeOpen(true)
+ fakeKeyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ to = KeyguardState.GONE,
+ testScope = this
+ )
+ // Scene change will be handled in EditWidgetsActivity not here
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
+ }
+ }
+
@Ignore("Ignored until custom animations are implemented in b/322787129")
@Test
fun deviceDocked_forceCommunalScene() =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index e42a67b..3d454a2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -52,6 +52,7 @@
import com.android.systemui.communal.shared.model.CommunalContentSize
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
+import com.android.systemui.communal.shared.model.EditModeState
import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.EnableSceneContainer
@@ -121,6 +122,7 @@
private lateinit var communalPrefsRepository: FakeCommunalPrefsRepository
private lateinit var editWidgetsActivityStarter: EditWidgetsActivityStarter
private lateinit var sceneInteractor: SceneInteractor
+ private lateinit var communalSceneInteractor: CommunalSceneInteractor
private lateinit var userTracker: FakeUserTracker
private lateinit var activityStarter: ActivityStarter
private lateinit var userManager: UserManager
@@ -141,6 +143,7 @@
editWidgetsActivityStarter = kosmos.editWidgetsActivityStarter
communalPrefsRepository = kosmos.fakeCommunalPrefsRepository
sceneInteractor = kosmos.sceneInteractor
+ communalSceneInteractor = kosmos.communalSceneInteractor
userTracker = kosmos.fakeUserTracker
activityStarter = kosmos.activityStarter
userManager = kosmos.userManager
@@ -815,7 +818,11 @@
@Test
fun testShowWidgetEditorStartsActivity() =
testScope.runTest {
+ val editModeState by collectLastValue(communalSceneInteractor.editModeState)
+
underTest.showWidgetEditor()
+
+ assertThat(editModeState).isEqualTo(EditModeState.STARTING)
verify(editWidgetsActivityStarter).startActivity()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt
index aad2e60..6e48b99 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt
@@ -20,15 +20,19 @@
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.communal.data.repository.communalSceneRepository
import com.android.systemui.communal.domain.model.CommunalTransitionProgressModel
import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.communal.shared.model.EditModeState
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -63,6 +67,42 @@
assertThat(currentScene).isEqualTo(CommunalScenes.Communal)
}
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ fun snapToSceneWithDelay() =
+ testScope.runTest {
+ val currentScene by collectLastValue(underTest.currentScene)
+ assertThat(currentScene).isEqualTo(CommunalScenes.Blank)
+ underTest.snapToScene(
+ CommunalScenes.Communal,
+ ActivityTransitionAnimator.TIMINGS.totalDuration
+ )
+ assertThat(currentScene).isEqualTo(CommunalScenes.Blank)
+ advanceTimeBy(ActivityTransitionAnimator.TIMINGS.totalDuration)
+ assertThat(currentScene).isEqualTo(CommunalScenes.Communal)
+ }
+
+ @Test
+ fun snapToSceneForActivity() =
+ testScope.runTest {
+ val currentScene by collectLastValue(underTest.currentScene)
+ assertThat(currentScene).isEqualTo(CommunalScenes.Blank)
+
+ underTest.snapToSceneForActivityStart(CommunalScenes.Communal)
+ assertThat(currentScene).isEqualTo(CommunalScenes.Communal)
+ }
+
+ @Test
+ fun snapToSceneForActivity_willNotChangeScene_forEditModeActivity() =
+ testScope.runTest {
+ val currentScene by collectLastValue(underTest.currentScene)
+ assertThat(currentScene).isEqualTo(CommunalScenes.Blank)
+
+ underTest.setEditModeState(EditModeState.STARTING)
+ underTest.snapToSceneForActivityStart(CommunalScenes.Communal)
+ assertThat(currentScene).isEqualTo(CommunalScenes.Blank)
+ }
+
@Test
fun transitionProgress_fullProgress() =
testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandlerTest.kt
new file mode 100644
index 0000000..0cd3fb2
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandlerTest.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.smartspace
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.view.View
+import android.widget.FrameLayout
+import android.widget.RemoteViews.RemoteResponse
+import androidx.core.util.component1
+import androidx.core.util.component2
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.widgets.SmartspaceAppWidgetHostView
+import com.android.systemui.plugins.ActivityStarter
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.isNull
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.notNull
+import org.mockito.kotlin.refEq
+import org.mockito.kotlin.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SmartspaceInteractionHandlerTest : SysuiTestCase() {
+ private val activityStarter = mock<ActivityStarter>()
+
+ private val testIntent =
+ PendingIntent.getActivity(
+ context,
+ /* requestCode = */ 0,
+ Intent("action"),
+ PendingIntent.FLAG_IMMUTABLE
+ )
+ private val testResponse = RemoteResponse.fromPendingIntent(testIntent)
+
+ private val underTest: SmartspaceInteractionHandler by lazy {
+ SmartspaceInteractionHandler(activityStarter)
+ }
+
+ @Test
+ fun launchAnimatorIsUsedForSmartspaceView() {
+ val parent = FrameLayout(context)
+ val view = SmartspaceAppWidgetHostView(context)
+ parent.addView(view)
+ val (fillInIntent, activityOptions) = testResponse.getLaunchOptions(view)
+
+ underTest.onInteraction(view, testIntent, testResponse)
+
+ // Verify that we pass in a non-null animation controller
+ verify(activityStarter)
+ .startPendingIntentWithoutDismissing(
+ /* intent = */ eq(testIntent),
+ /* dismissShade = */ eq(false),
+ /* intentSentUiThreadCallback = */ isNull(),
+ /* animationController = */ notNull(),
+ /* fillInIntent = */ refEq(fillInIntent),
+ /* extraOptions = */ refEq(activityOptions.toBundle()),
+ )
+ }
+
+ @Test
+ fun launchAnimatorIsNotUsedForRegularView() {
+ val parent = FrameLayout(context)
+ val view = View(context)
+ parent.addView(view)
+ val (fillInIntent, activityOptions) = testResponse.getLaunchOptions(view)
+
+ underTest.onInteraction(view, testIntent, testResponse)
+
+ // Verify null is used as the animation controller
+ verify(activityStarter)
+ .startPendingIntentWithoutDismissing(
+ /* intent = */ eq(testIntent),
+ /* dismissShade = */ eq(false),
+ /* intentSentUiThreadCallback = */ isNull(),
+ /* animationController = */ isNull(),
+ /* fillInIntent = */ refEq(fillInIntent),
+ /* extraOptions = */ refEq(activityOptions.toBundle()),
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelTest.kt
index f9d5073..0250c9d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelTest.kt
@@ -19,17 +19,23 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
+import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class CommunalTransitionViewModelTest : SysuiTestCase() {
@@ -49,13 +55,9 @@
fun testIsUmoOnCommunalDuringTransitionBetweenLockscreenAndGlanceableHub() =
testScope.runTest {
val isUmoOnCommunal by collectLastValue(underTest.isUmoOnCommunal)
- assertThat(isUmoOnCommunal).isNull()
+ runCurrent()
- keyguardTransitionRepository.sendTransitionSteps(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.GLANCEABLE_HUB,
- testScope
- )
+ enterCommunal(from = KeyguardState.LOCKSCREEN)
assertThat(isUmoOnCommunal).isTrue()
keyguardTransitionRepository.sendTransitionSteps(
@@ -70,13 +72,9 @@
fun testIsUmoOnCommunalDuringTransitionBetweenDreamingAndGlanceableHub() =
testScope.runTest {
val isUmoOnCommunal by collectLastValue(underTest.isUmoOnCommunal)
- assertThat(isUmoOnCommunal).isNull()
+ runCurrent()
- keyguardTransitionRepository.sendTransitionSteps(
- from = KeyguardState.DREAMING,
- to = KeyguardState.GLANCEABLE_HUB,
- testScope
- )
+ enterCommunal(from = KeyguardState.DREAMING)
assertThat(isUmoOnCommunal).isTrue()
keyguardTransitionRepository.sendTransitionSteps(
@@ -91,13 +89,9 @@
fun testIsUmoOnCommunalDuringTransitionBetweenOccludedAndGlanceableHub() =
testScope.runTest {
val isUmoOnCommunal by collectLastValue(underTest.isUmoOnCommunal)
- assertThat(isUmoOnCommunal).isNull()
+ runCurrent()
- keyguardTransitionRepository.sendTransitionSteps(
- from = KeyguardState.OCCLUDED,
- to = KeyguardState.GLANCEABLE_HUB,
- testScope
- )
+ enterCommunal(from = KeyguardState.OCCLUDED)
assertThat(isUmoOnCommunal).isTrue()
keyguardTransitionRepository.sendTransitionSteps(
@@ -105,7 +99,33 @@
to = KeyguardState.OCCLUDED,
testScope
)
-
assertThat(isUmoOnCommunal).isFalse()
}
+
+ @Test
+ fun isUmoOnCommunal_noLongerVisible_returnsFalse() =
+ testScope.runTest {
+ val isUmoOnCommunal by collectLastValue(underTest.isUmoOnCommunal)
+ runCurrent()
+
+ enterCommunal(from = KeyguardState.LOCKSCREEN)
+ assertThat(isUmoOnCommunal).isTrue()
+
+ // Communal is no longer visible.
+ kosmos.fakeCommunalSceneRepository.changeScene(CommunalScenes.Blank)
+ runCurrent()
+
+ // isUmoOnCommunal returns false, even without any keyguard transition.
+ assertThat(isUmoOnCommunal).isFalse()
+ }
+
+ private suspend fun TestScope.enterCommunal(from: KeyguardState) {
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = from,
+ to = KeyguardState.GLANCEABLE_HUB,
+ testScope
+ )
+ kosmos.fakeCommunalSceneRepository.changeScene(CommunalScenes.Communal)
+ runCurrent()
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
index 84dbfd4..d5fe2a1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
@@ -38,14 +38,17 @@
import com.android.systemui.communal.data.repository.fakeCommunalMediaRepository
import com.android.systemui.communal.data.repository.fakeCommunalTutorialRepository
import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.log.CommunalUiEvent
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
+import com.android.systemui.communal.shared.model.EditModeState
import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.logcatLogBuffer
@@ -86,6 +89,7 @@
private lateinit var widgetRepository: FakeCommunalWidgetRepository
private lateinit var smartspaceRepository: FakeSmartspaceRepository
private lateinit var mediaRepository: FakeCommunalMediaRepository
+ private lateinit var communalSceneInteractor: CommunalSceneInteractor
private val testableResources = context.orCreateTestableResources
@@ -99,6 +103,7 @@
widgetRepository = kosmos.fakeCommunalWidgetRepository
smartspaceRepository = kosmos.fakeSmartspaceRepository
mediaRepository = kosmos.fakeCommunalMediaRepository
+ communalSceneInteractor = kosmos.communalSceneInteractor
kosmos.fakeUserTracker.set(
userInfos = listOf(MAIN_USER_INFO),
selectedUserIndex = 0,
@@ -107,9 +112,10 @@
underTest =
CommunalEditModeViewModel(
- kosmos.communalSceneInteractor,
+ communalSceneInteractor,
kosmos.communalInteractor,
kosmos.communalSettingsInteractor,
+ kosmos.keyguardTransitionInteractor,
mediaHost,
uiEventLogger,
logcatLogBuffer("CommunalEditModeViewModelTest"),
@@ -172,6 +178,22 @@
}
@Test
+ fun isCommunalContentVisible_isTrue_whenEditModeShowing() =
+ testScope.runTest {
+ val isCommunalContentVisible by collectLastValue(underTest.isCommunalContentVisible)
+ communalSceneInteractor.setEditModeState(EditModeState.SHOWING)
+ assertThat(isCommunalContentVisible).isEqualTo(true)
+ }
+
+ @Test
+ fun isCommunalContentVisible_isFalse_whenEditModeNotShowing() =
+ testScope.runTest {
+ val isCommunalContentVisible by collectLastValue(underTest.isCommunalContentVisible)
+ communalSceneInteractor.setEditModeState(null)
+ assertThat(isCommunalContentVisible).isEqualTo(false)
+ }
+
+ @Test
fun deleteWidget() =
testScope.runTest {
tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt
index 420b11c..7b7d03b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt
@@ -27,23 +27,19 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.util.mockito.eq
-import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.refEq
-import org.mockito.Mock
-import org.mockito.Mockito.isNull
-import org.mockito.Mockito.notNull
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.isNull
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.notNull
+import org.mockito.kotlin.refEq
+import org.mockito.kotlin.verify
@SmallTest
@RunWith(AndroidJUnit4::class)
class WidgetInteractionHandlerTest : SysuiTestCase() {
- @Mock private lateinit var activityStarter: ActivityStarter
-
- private lateinit var underTest: WidgetInteractionHandler
+ private val activityStarter = mock<ActivityStarter>()
private val testIntent =
PendingIntent.getActivity(
@@ -54,10 +50,8 @@
)
private val testResponse = RemoteResponse.fromPendingIntent(testIntent)
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
- underTest = WidgetInteractionHandler(activityStarter)
+ private val underTest: WidgetInteractionHandler by lazy {
+ WidgetInteractionHandler(activityStarter)
}
@Test
@@ -69,14 +63,15 @@
underTest.onInteraction(view, testIntent, testResponse)
+ // Verify that we pass in a non-null animation controller
verify(activityStarter)
.startPendingIntentMaybeDismissingKeyguard(
- eq(testIntent),
- eq(false),
- isNull(),
- notNull(),
- refEq(fillInIntent),
- refEq(activityOptions.toBundle()),
+ /* intent = */ eq(testIntent),
+ /* dismissShade = */ eq(false),
+ /* intentSentUiThreadCallback = */ isNull(),
+ /* animationController = */ notNull(),
+ /* fillInIntent = */ refEq(fillInIntent),
+ /* extraOptions = */ refEq(activityOptions.toBundle()),
)
}
@@ -89,14 +84,15 @@
underTest.onInteraction(view, testIntent, testResponse)
+ // Verify null is used as the animation controller
verify(activityStarter)
.startPendingIntentMaybeDismissingKeyguard(
- eq(testIntent),
- eq(false),
- isNull(),
- isNull(),
- refEq(fillInIntent),
- refEq(activityOptions.toBundle()),
+ /* intent = */ eq(testIntent),
+ /* dismissShade = */ eq(false),
+ /* intentSentUiThreadCallback = */ isNull(),
+ /* animationController = */ isNull(),
+ /* fillInIntent = */ refEq(fillInIntent),
+ /* extraOptions = */ refEq(activityOptions.toBundle()),
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
index 86fdaa5..73ef775 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
@@ -7,6 +7,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController
import com.android.systemui.complication.ComplicationHostViewController
import com.android.systemui.dreams.ui.viewmodel.DreamViewModel
import com.android.systemui.log.core.FakeLogBuffer
@@ -43,7 +44,7 @@
@Mock private lateinit var mockAnimator: AnimatorSet
@Mock private lateinit var blurUtils: BlurUtils
@Mock private lateinit var hostViewController: ComplicationHostViewController
- @Mock private lateinit var statusBarViewController: DreamOverlayStatusBarViewController
+ @Mock private lateinit var statusBarViewController: AmbientStatusBarViewController
@Mock private lateinit var stateController: DreamOverlayStateController
@Mock private lateinit var transitionViewModel: DreamViewModel
private val logBuffer = FakeLogBuffer.Factory.create()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
index f5c86e0..c48ced1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.dreams;
import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
+import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -47,6 +48,7 @@
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController;
import com.android.systemui.ambient.touch.scrim.BouncerlessScrimController;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
@@ -55,6 +57,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.BlurUtils;
+import com.android.systemui.touch.TouchInsetManager;
import kotlinx.coroutines.CoroutineDispatcher;
@@ -80,7 +83,7 @@
ViewTreeObserver mViewTreeObserver;
@Mock
- DreamOverlayStatusBarViewController mDreamOverlayStatusBarViewController;
+ AmbientStatusBarViewController mAmbientStatusBarViewController;
@Mock
LowLightTransitionCoordinator mLowLightTransitionCoordinator;
@@ -131,6 +134,8 @@
CommunalInteractor mCommunalInteractor;
@Mock
private DreamManager mDreamManager;
+ @Mock
+ private TouchInsetManager.TouchInsetSession mTouchInsetSession;
DreamOverlayContainerViewController mController;
@@ -144,14 +149,17 @@
when(mDreamOverlayContainerView.getRootSurfaceControl())
.thenReturn(mAttachedSurfaceControl);
when(mKeyguardTransitionInteractor.isFinishedInStateWhere(any())).thenReturn(emptyFlow());
+ when(mShadeInteractor.isAnyExpanded()).thenReturn(MutableStateFlow(false));
+ when(mCommunalInteractor.isCommunalShowing()).thenReturn(MutableStateFlow(false));
mController = new DreamOverlayContainerViewController(
mDreamOverlayContainerView,
mComplicationHostViewController,
mDreamOverlayContentView,
mHubGestureIndicatorView,
- mDreamOverlayStatusBarViewController,
+ mAmbientStatusBarViewController,
mLowLightTransitionCoordinator,
+ mTouchInsetSession,
mBlurUtils,
mHandler,
mDispatcher,
@@ -190,7 +198,7 @@
@Test
public void testDreamOverlayStatusBarViewControllerInitialized() {
mController.init();
- verify(mDreamOverlayStatusBarViewController).init();
+ verify(mAmbientStatusBarViewController).init();
}
@Test
@@ -325,4 +333,12 @@
mController.onViewDetached();
verify(mBouncerlessScrimController).removeCallback(any());
}
+
+ @EnableFlags(android.service.dreams.Flags.FLAG_DREAM_HANDLES_BEING_OBSCURED)
+ @Test
+ public void testOnViewAttachedSucceedsWhenDreamHandlesBeingObscuredFlagEnabled() {
+ // This test will catch failures in presubmit when the dream_handles_being_obscured flag is
+ // enabled.
+ mController.onViewAttached();
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
index c51413a..3d3c778 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
@@ -21,26 +21,35 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.haptics.vibratorHelper
-import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.qsTileFactory
+import com.android.systemui.statusbar.policy.keyguardStateController
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
@RunWithLooper(setAsMainLooper = true)
class QSLongPressEffectTest : SysuiTestCase() {
+ @Rule @JvmField val mMockitoRule: MockitoRule = MockitoJUnit.rule()
private val kosmos = testKosmos()
private val vibratorHelper = kosmos.vibratorHelper
+ private val qsTile = kosmos.qsTileFactory.createTile("Test Tile")
+ @Mock private lateinit var callback: QSLongPressEffect.Callback
private val effectDuration = 400
private val lowTickDuration = 12
@@ -54,13 +63,15 @@
lowTickDuration
vibratorHelper.primitiveDurations[VibrationEffect.Composition.PRIMITIVE_SPIN] = spinDuration
- kosmos.fakeKeyguardRepository.setKeyguardDismissible(true)
+ whenever(kosmos.keyguardStateController.isUnlocked).thenReturn(true)
longPressEffect =
QSLongPressEffect(
vibratorHelper,
- kosmos.keyguardInteractor,
+ kosmos.keyguardStateController,
)
+ longPressEffect.callback = callback
+ longPressEffect.qsTile = qsTile
}
@Test
@@ -107,28 +118,13 @@
}
@Test
- fun onActionUp_whileWaiting_performsClick() =
- testWhileInState(QSLongPressEffect.State.TIMEOUT_WAIT) {
- // GIVEN an action is being collected
- val action by collectLastValue(longPressEffect.actionType)
-
- // GIVEN an action up occurs
- longPressEffect.handleActionUp()
-
- // THEN the action to invoke is the click action and the effect does not start
- assertThat(action).isEqualTo(QSLongPressEffect.ActionType.CLICK)
- assertEffectDidNotStart()
- }
-
- @Test
fun onWaitComplete_whileWaiting_beginsEffect() =
testWhileInState(QSLongPressEffect.State.TIMEOUT_WAIT) {
// GIVEN the pressed timeout is complete
longPressEffect.handleTimeoutComplete()
// THEN the effect emits the action to start an animator
- val action by collectLastValue(longPressEffect.actionType)
- assertThat(action).isEqualTo(QSLongPressEffect.ActionType.START_ANIMATOR)
+ verify(callback, times(1)).onStartAnimator()
}
@Test
@@ -179,26 +175,28 @@
}
@Test
- fun onAnimationComplete_keyguardDismissible_effectEndsWithLongPress() =
+ fun onAnimationComplete_keyguardDismissible_effectEndsWithPrepare() =
testWhileInState(QSLongPressEffect.State.RUNNING_FORWARD) {
// GIVEN that the animation completes
longPressEffect.handleAnimationComplete()
- // THEN the long-press effect completes with a LONG_PRESS
- assertEffectCompleted(QSLongPressEffect.ActionType.LONG_PRESS)
+ // THEN the long-press effect completes and the view is called to prepare
+ assertEffectCompleted()
+ verify(callback, times(1)).onPrepareForLaunch()
}
@Test
- fun onAnimationComplete_keyguardNotDismissible_effectEndsWithResetAndLongPress() =
+ fun onAnimationComplete_keyguardNotDismissible_effectEndsWithReset() =
testWhileInState(QSLongPressEffect.State.RUNNING_FORWARD) {
// GIVEN that the keyguard is not dismissible
- kosmos.fakeKeyguardRepository.setKeyguardDismissible(false)
+ whenever(kosmos.keyguardStateController.isUnlocked).thenReturn(false)
// GIVEN that the animation completes
longPressEffect.handleAnimationComplete()
- // THEN the long-press effect completes with RESET_AND_LONG_PRESS
- assertEffectCompleted(QSLongPressEffect.ActionType.RESET_AND_LONG_PRESS)
+ // THEN the long-press effect completes and the properties are called to reset
+ assertEffectCompleted()
+ verify(callback, times(1)).onResetProperties()
}
@Test
@@ -211,8 +209,7 @@
longPressEffect.handleActionDown()
// THEN the effect posts an action to cancel the animator
- val action by collectLastValue(longPressEffect.actionType)
- assertThat(action).isEqualTo(QSLongPressEffect.ActionType.CANCEL_ANIMATOR)
+ verify(callback, times(1)).onCancelAnimator()
}
@Test
@@ -238,6 +235,29 @@
assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.IDLE)
}
+ @Test
+ fun onTileClick_whileWaiting_withQSTile_clicks() =
+ testWhileInState(QSLongPressEffect.State.TIMEOUT_WAIT) {
+ // GIVEN that a click was detected
+ val couldClick = longPressEffect.onTileClick()
+
+ // THEN the click is successful
+ assertThat(couldClick).isTrue()
+ }
+
+ @Test
+ fun onTileClick_whileWaiting_withoutQSTile_cannotClick() =
+ testWhileInState(QSLongPressEffect.State.TIMEOUT_WAIT) {
+ // GIVEN that no QSTile has been set
+ longPressEffect.qsTile = null
+
+ // GIVEN that a click was detected
+ val couldClick = longPressEffect.onTileClick()
+
+ // THEN the click is not successful
+ assertThat(couldClick).isFalse()
+ }
+
private fun testWithScope(initialize: Boolean = true, test: suspend TestScope.() -> Unit) =
with(kosmos) {
testScope.runTest {
@@ -300,16 +320,13 @@
* Asserts that the effect completes by checking that:
* 1. The final snap haptics are played
* 2. The internal state goes back to [QSLongPressEffect.State.IDLE]
- * 3. The action to perform on the tile is the action given as a parameter
*/
- private fun TestScope.assertEffectCompleted(expectedAction: QSLongPressEffect.ActionType) {
- val action by collectLastValue(longPressEffect.actionType)
+ private fun assertEffectCompleted() {
val snapEffect = LongPressHapticBuilder.createSnapEffect()
assertThat(snapEffect).isNotNull()
assertThat(vibratorHelper.hasVibratedWithEffects(snapEffect!!)).isTrue()
assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.IDLE)
- assertThat(action).isEqualTo(expectedAction)
}
/**
@@ -317,10 +334,8 @@
* 1. The internal state is [QSLongPressEffect.State.RUNNING_BACKWARDS]
* 2. An action to reverse the animator is emitted
*/
- private fun TestScope.assertEffectReverses() {
- val action by collectLastValue(longPressEffect.actionType)
-
+ private fun assertEffectReverses() {
assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.RUNNING_BACKWARDS)
- assertThat(action).isEqualTo(QSLongPressEffect.ActionType.REVERSE_ANIMATOR)
+ verify(callback, times(1)).onReverseAnimator()
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index f375ec7..5dac37a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -333,27 +333,16 @@
}
@Test
- fun isDreamingFromKeyguardUpdateMonitor() =
- TestScope(mainDispatcher).runTest {
- whenever(keyguardUpdateMonitor.isDreaming()).thenReturn(false)
- var latest: Boolean? = null
- val job = underTest.isDreaming.onEach { latest = it }.launchIn(this)
+ fun isDreaming() =
+ testScope.runTest {
+ val isDreaming by collectLastValue(underTest.isDreaming)
+ assertThat(isDreaming).isFalse()
- runCurrent()
- assertThat(latest).isFalse()
+ underTest.setDreaming(true)
+ assertThat(isDreaming).isTrue()
- val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
- verify(keyguardUpdateMonitor).registerCallback(captor.capture())
-
- captor.value.onDreamingStateChanged(true)
- runCurrent()
- assertThat(latest).isTrue()
-
- captor.value.onDreamingStateChanged(false)
- runCurrent()
- assertThat(latest).isFalse()
-
- job.cancel()
+ underTest.setDreaming(false)
+ assertThat(isDreaming).isFalse()
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
index cfc6b33..d20fec4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
@@ -32,10 +32,14 @@
package com.android.systemui.keyguard.domain.interactor
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
+import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -76,6 +80,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
fun transitionToGone_keyguardOccluded_biometricAuthenticated() =
testScope.runTest {
transitionRepository.sendTransitionSteps(
@@ -96,6 +101,25 @@
}
@Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun transitionToGone_keyguardOccludedThenAltBouncer_authed_wmStateRefactor() =
+ testScope.runTest {
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.OCCLUDED,
+ to = KeyguardState.ALTERNATE_BOUNCER,
+ testScope
+ )
+ reset(transitionRepository)
+
+ // Authentication results in calling startDismissKeyguardTransition.
+ kosmos.keyguardTransitionInteractor.startDismissKeyguardTransition()
+ runCurrent()
+
+ assertThat(transitionRepository)
+ .startedTransition(from = KeyguardState.ALTERNATE_BOUNCER, to = KeyguardState.GONE)
+ }
+
+ @Test
fun noTransition_keyguardNotOccluded_biometricAuthenticated() =
testScope.runTest {
transitionRepository.sendTransitionSteps(
@@ -143,4 +167,37 @@
to = KeyguardState.OCCLUDED
)
}
+
+ @Test
+ fun transitionToGone_whenOpeningGlanceableHubEditMode() =
+ testScope.runTest {
+ kosmos.fakeKeyguardBouncerRepository.setAlternateVisible(true)
+ runCurrent()
+
+ // On Glanceable hub and edit mode activity is started
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.ALTERNATE_BOUNCER,
+ testScope
+ )
+ reset(transitionRepository)
+
+ kosmos.communalInteractor.setEditModeOpen(true)
+ runCurrent()
+
+ // Auth and alternate bouncer is hidden
+ kosmos.fakeKeyguardBouncerRepository.setAlternateVisible(false)
+ advanceTimeBy(200) // advance past delay
+
+ // Then no transition should occur yet
+ assertThat(transitionRepository).noTransitionsStarted()
+
+ // When keyguard is going away
+ kosmos.fakeKeyguardRepository.setKeyguardGoingAway(true)
+ runCurrent()
+
+ // Then transition to GONE should occur
+ assertThat(transitionRepository)
+ .startedTransition(from = KeyguardState.ALTERNATE_BOUNCER, to = KeyguardState.GONE)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
index 6c5001a..6eb9862 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
@@ -53,6 +53,7 @@
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
import com.android.systemui.testKosmos
import junit.framework.Assert.assertEquals
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -299,6 +300,7 @@
fun testTransitionToOccluded_onWake() =
testScope.runTest {
kosmos.fakeKeyguardRepository.setKeyguardOccluded(true)
+ kosmos.keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(true)
powerInteractor.setAwakeForTest()
advanceTimeBy(100) // account for debouncing
@@ -312,6 +314,7 @@
testScope.runTest {
kosmos.fakeKeyguardRepository.setKeyguardShowing(false)
kosmos.fakeKeyguardRepository.setKeyguardDismissible(true)
+ kosmos.keyguardTransitionInteractor.startDismissKeyguardTransition()
powerInteractor.setAwakeForTest()
advanceTimeBy(100) // account for debouncing
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index addbdb6..7906a82 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -27,6 +27,7 @@
import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeCommandQueue
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
@@ -191,6 +192,7 @@
fun dismissAlpha() =
testScope.runTest {
val dismissAlpha by collectLastValue(underTest.dismissAlpha)
+ assertThat(dismissAlpha).isEqualTo(1f)
keyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.AOD,
@@ -202,9 +204,9 @@
// User begins to swipe up
shadeRepository.setLegacyShadeExpansion(0.99f)
- // When not dismissable, no alpha value (null) should emit
+ // When not dismissable, the last alpha value should still be present
repository.setKeyguardDismissible(false)
- assertThat(dismissAlpha).isNull()
+ assertThat(dismissAlpha).isEqualTo(1f)
repository.setKeyguardDismissible(true)
shadeRepository.setLegacyShadeExpansion(0.98f)
@@ -212,9 +214,11 @@
}
@Test
- fun dismissAlpha_whenShadeIsExpandedEmitsNull() =
+ fun dismissAlpha_whenShadeResetsEmitsOne() =
testScope.runTest {
- val dismissAlpha by collectLastValue(underTest.dismissAlpha)
+ val dismissAlpha by collectValues(underTest.dismissAlpha)
+ assertThat(dismissAlpha[0]).isEqualTo(1f)
+ assertThat(dismissAlpha.size).isEqualTo(1)
keyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.AOD,
@@ -222,14 +226,50 @@
testScope,
)
- repository.setStatusBarState(StatusBarState.SHADE_LOCKED)
- shadeRepository.setQsExpansion(1f)
+ // User begins to swipe up
+ repository.setStatusBarState(StatusBarState.KEYGUARD)
+ repository.setKeyguardDismissible(true)
+ shadeRepository.setLegacyShadeExpansion(0.98f)
- repository.setKeyguardDismissible(false)
- assertThat(dismissAlpha).isNull()
+ assertThat(dismissAlpha[1]).isGreaterThan(0.5f)
+ assertThat(dismissAlpha[1]).isLessThan(1f)
+ assertThat(dismissAlpha.size).isEqualTo(2)
+
+ // Now reset the shade
+ shadeRepository.setLegacyShadeExpansion(1f)
+ assertThat(dismissAlpha[2]).isEqualTo(1f)
+ assertThat(dismissAlpha.size).isEqualTo(3)
+ }
+
+ @Test
+ fun dismissAlpha_doesNotEmitWhileTransitioning() =
+ testScope.runTest {
+ val dismissAlpha by collectLastValue(underTest.dismissAlpha)
+ assertThat(dismissAlpha).isEqualTo(1f)
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ listOf(
+ TransitionStep(
+ from = KeyguardState.AOD,
+ to = KeyguardState.GONE,
+ value = 0f,
+ transitionState = TransitionState.STARTED,
+ ),
+ TransitionStep(
+ from = KeyguardState.AOD,
+ to = KeyguardState.GONE,
+ value = 0.1f,
+ transitionState = TransitionState.RUNNING,
+ ),
+ ),
+ testScope,
+ )
repository.setKeyguardDismissible(true)
- assertThat(dismissAlpha).isNull()
+ shadeRepository.setLegacyShadeExpansion(0.98f)
+
+ // Should still be one
+ assertThat(dismissAlpha).isEqualTo(1f)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
index 5756bca..0f061de 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
@@ -125,68 +125,6 @@
}
@Test
- fun dozeAmountTransitionTest_AodToFromLockscreen() =
- testScope.runTest {
- val dozeAmountSteps by collectValues(underTest.dozeAmountTransition)
-
- val steps = mutableListOf<TransitionStep>()
-
- steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
- steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
- steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 0.8f, RUNNING))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
-
- steps.forEach {
- repository.sendTransitionStep(it)
- runCurrent()
- }
-
- assertThat(dozeAmountSteps.subList(0, 3))
- .isEqualTo(
- listOf(
- steps[0].copy(value = 1f - steps[0].value),
- steps[1].copy(value = 1f - steps[1].value),
- steps[2].copy(value = 1f - steps[2].value),
- )
- )
- assertThat(dozeAmountSteps.subList(3, 7)).isEqualTo(steps.subList(3, 7))
- }
-
- @Test
- fun dozeAmountTransitionTest_AodToFromGone() =
- testScope.runTest {
- val dozeAmountSteps by collectValues(underTest.dozeAmountTransition)
-
- val steps = mutableListOf<TransitionStep>()
-
- steps.add(TransitionStep(AOD, GONE, 0f, STARTED))
- steps.add(TransitionStep(AOD, GONE, 0.3f, RUNNING))
- steps.add(TransitionStep(AOD, GONE, 1f, FINISHED))
- steps.add(TransitionStep(GONE, AOD, 0f, STARTED))
- steps.add(TransitionStep(GONE, AOD, 0.1f, RUNNING))
- steps.add(TransitionStep(GONE, AOD, 0.3f, RUNNING))
- steps.add(TransitionStep(GONE, AOD, 1f, FINISHED))
-
- steps.forEach {
- repository.sendTransitionStep(it)
- runCurrent()
- }
-
- assertThat(dozeAmountSteps.subList(0, 3))
- .isEqualTo(
- listOf(
- steps[0].copy(value = 1f - steps[0].value),
- steps[1].copy(value = 1f - steps[1].value),
- steps[2].copy(value = 1f - steps[2].value),
- )
- )
- assertThat(dozeAmountSteps.subList(3, 7)).isEqualTo(steps.subList(3, 7))
- }
-
- @Test
fun finishedKeyguardStateTests() =
testScope.runTest {
val finishedSteps by collectValues(underTest.finishedKeyguardState)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
index 9dc930b..6e16705 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
@@ -197,7 +197,7 @@
@Test
@EnableSceneContainer
- fun surfaceBehindVisibility_fromLockscreenToGone_trueThroughout() =
+ fun surfaceBehindVisibility_fromLockscreenToGone_noUserInput_trueThroughout() =
testScope.runTest {
val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility)
val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
@@ -249,6 +249,43 @@
@Test
@EnableSceneContainer
+ fun surfaceBehindVisibility_fromLockscreenToGone_withUserInput_falseUntilInputStops() =
+ testScope.runTest {
+ val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility)
+ val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
+
+ // Before the transition, we start on Lockscreen so the surface should start invisible.
+ kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Lockscreen))
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(isSurfaceBehindVisible).isFalse()
+
+ // Unlocked with fingerprint.
+ kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+ SuccessFingerprintAuthenticationStatus(0, true)
+ )
+
+ // Start the transition to Gone, the surface should not be visible while
+ // isUserInputOngoing is true
+ val isUserInputOngoing = MutableStateFlow(true)
+ kosmos.setSceneTransition(
+ ObservableTransitionState.Transition(
+ fromScene = Scenes.Lockscreen,
+ toScene = Scenes.Gone,
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = isUserInputOngoing,
+ progress = flowOf(0.51f),
+ currentScene = flowOf(Scenes.Gone),
+ )
+ )
+ assertThat(isSurfaceBehindVisible).isFalse()
+
+ // When isUserInputOngoing becomes false, then the surface should become visible.
+ isUserInputOngoing.value = false
+ assertThat(isSurfaceBehindVisible).isTrue()
+ }
+
+ @Test
+ @EnableSceneContainer
fun surfaceBehindVisibility_fromBouncerToGone_becomesTrue() =
testScope.runTest {
val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModelTest.kt
index 460a1fc..b0959e4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModelTest.kt
@@ -25,6 +25,7 @@
import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -58,6 +59,62 @@
assertThat(viewModel?.tint).isEqualTo(Color.WHITE)
}
+ @Test
+ fun startsDozing_doNotShowAodVariant() =
+ testScope.runTest {
+ val viewModel by collectLastValue(underTest.viewModel)
+
+ givenUdfpsEnrolledAndEnabled()
+ kosmos.run {
+ fakeKeyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.DOZING,
+ testScope = testScope,
+ throughTransitionState = TransitionState.STARTED,
+ )
+ }
+
+ assertThat(viewModel?.useAodVariant).isEqualTo(false)
+ }
+
+ @Test
+ fun finishedDozing_showAodVariant() =
+ testScope.runTest {
+ val viewModel by collectLastValue(underTest.viewModel)
+
+ givenUdfpsEnrolledAndEnabled()
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ testScope = testScope,
+ throughTransitionState = TransitionState.FINISHED,
+ )
+
+ assertThat(viewModel?.useAodVariant).isEqualTo(true)
+ }
+
+ @Test
+ fun startTransitionToLockscreenFromDozing_doNotShowAodVariant() =
+ testScope.runTest {
+ val viewModel by collectLastValue(underTest.viewModel)
+
+ givenUdfpsEnrolledAndEnabled()
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.DOZING,
+ testScope = testScope,
+ throughTransitionState = TransitionState.FINISHED,
+ )
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.DOZING,
+ to = KeyguardState.LOCKSCREEN,
+ testScope = testScope,
+ throughTransitionState = TransitionState.RUNNING,
+ )
+
+ assertThat(viewModel?.useAodVariant).isEqualTo(false)
+ }
+
private fun givenUdfpsEnrolledAndEnabled() {
kosmos.fakeFingerprintPropertyRepository.supportsUdfps()
kosmos.fakeBiometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt
index 68fbd1c..3f93401 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt
@@ -227,11 +227,11 @@
assertThat(accessibilityDelegateHint)
.isEqualTo(DeviceEntryIconView.AccessibilityHintType.AUTHENTICATE)
- // non-interactive lock icon
+ // interactive lock icon for non udfps as well so that user can navigate to bouncer
fingerprintPropertyRepository.supportsRearFps()
assertThat(accessibilityDelegateHint)
- .isEqualTo(DeviceEntryIconView.AccessibilityHintType.NONE)
+ .isEqualTo(DeviceEntryIconView.AccessibilityHintType.AUTHENTICATE)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModelTest.kt
index 79671b8..bf3231e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModelTest.kt
@@ -53,7 +53,7 @@
@Test
fun lockscreenAlpha() =
testScope.runTest {
- val viewState = ViewStateAccessor(alpha = { 0.6f })
+ val viewState = ViewStateAccessor()
val alpha by collectValues(underTest.lockscreenAlpha(viewState))
keyguardTransitionRepository.sendTransitionSteps(
@@ -62,11 +62,9 @@
testScope
)
- assertThat(alpha[0]).isEqualTo(0.6f)
- // Fades out just prior to halfway
+ // Remain at zero throughout
+ assertThat(alpha[0]).isEqualTo(0f)
assertThat(alpha[1]).isEqualTo(0f)
- // Must finish at 0
- assertThat(alpha[2]).isEqualTo(0f)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModelTest.kt
index 59a6ce7..80a9532 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModelTest.kt
@@ -21,6 +21,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
import com.android.systemui.biometrics.data.repository.fakeFingerprintPropertyRepository
+import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
@@ -32,6 +33,7 @@
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
+import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
@@ -104,6 +106,24 @@
values.forEach { assertThat(it).isNull() }
}
+ @Test
+ fun notificationAlpha_fadesOut() =
+ testScope.runTest {
+ val alpha by collectLastValue(underTest.notificationAlpha)
+
+ keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(alpha).isEqualTo(1f)
+
+ keyguardTransitionRepository.sendTransitionStep(step(0.25f))
+ assertThat(alpha).isIn(Range.open(.25f, .75f))
+
+ keyguardTransitionRepository.sendTransitionStep(step(1f))
+ assertThat(alpha).isEqualTo(0f)
+
+ keyguardTransitionRepository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+ assertThat(alpha).isEqualTo(1f)
+ }
+
private fun step(value: Float, state: TransitionState = RUNNING): TransitionStep {
return TransitionStep(
from = KeyguardState.GONE,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 49df345..194f362 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -39,7 +39,9 @@
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.data.repository.Idle
import com.android.systemui.scene.data.repository.sceneContainerRepository
+import com.android.systemui.scene.data.repository.setSceneTransition
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.shadeTestUtil
@@ -290,6 +292,7 @@
testScope,
)
+ kosmos.setSceneTransition(Idle(Scenes.Gone))
// Make sure the value hasn't changed since we're GONE
keyguardRepository.topClippingBounds.value = 5
assertThat(topClippingBounds).isEqualTo(1000)
@@ -518,11 +521,14 @@
to = KeyguardState.GONE,
testScope = testScope,
)
+ kosmos.setSceneTransition(Idle(Scenes.Gone))
assertThat(alpha).isEqualTo(0f)
- // Try pulling down shade and ensure the value doesn't change
- shadeTestUtil.setQsExpansion(0.5f)
- assertThat(alpha).isEqualTo(0f)
+ if (!SceneContainerFlag.isEnabled) {
+ // Try pulling down shade and ensure the value doesn't change
+ shadeTestUtil.setQsExpansion(0.5f)
+ assertThat(alpha).isEqualTo(0f)
+ }
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index f46ca00..61d8216 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -50,6 +50,7 @@
import com.google.common.truth.Truth.assertThat
import kotlin.math.pow
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.junit.BeforeClass
import org.junit.Test
@@ -205,8 +206,13 @@
pointerCount = if (downWithTwoPointers) 2 else 1,
)
)
-
- assertThat(downDestination?.toScene)
+ val downScene by
+ collectLastValue(
+ downDestination?.let {
+ kosmos.sceneInteractor.resolveSceneFamily(downDestination.toScene)
+ } ?: flowOf(null)
+ )
+ assertThat(downScene)
.isEqualTo(
expectedDownDestination(
downFromEdge = downFromEdge,
@@ -223,7 +229,14 @@
)
)
- assertThat(destinationScenes?.get(Swipe(SwipeDirection.Up))?.toScene)
+ val upScene by
+ collectLastValue(
+ destinationScenes?.get(Swipe(SwipeDirection.Up))?.toScene?.let { scene ->
+ kosmos.sceneInteractor.resolveSceneFamily(scene)
+ } ?: flowOf(null)
+ )
+
+ assertThat(upScene)
.isEqualTo(
expectedUpDestination(
canSwipeToEnter = canSwipeToEnter,
@@ -231,7 +244,14 @@
)
)
- assertThat(destinationScenes?.get(Swipe(SwipeDirection.Left))?.toScene)
+ val leftScene by
+ collectLastValue(
+ destinationScenes?.get(Swipe(SwipeDirection.Left))?.toScene?.let { scene ->
+ kosmos.sceneInteractor.resolveSceneFamily(scene)
+ } ?: flowOf(null)
+ )
+
+ assertThat(leftScene)
.isEqualTo(
expectedLeftDestination(
isCommunalAvailable = isCommunalAvailable,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
index 7a37a9e..bc0512a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
@@ -56,11 +56,15 @@
underTest.addSelectedUserMediaEntry(userMedia)
assertThat(selectedUserEntries?.get(instanceId)).isEqualTo(userMedia)
+ assertThat(underTest.hasActiveMedia()).isTrue()
+ assertThat(underTest.hasAnyMedia()).isTrue()
underTest.addSelectedUserMediaEntry(userMedia.copy(active = false))
assertThat(selectedUserEntries?.get(instanceId)).isNotEqualTo(userMedia)
assertThat(selectedUserEntries?.get(instanceId)?.active).isFalse()
+ assertThat(underTest.hasActiveMedia()).isFalse()
+ assertThat(underTest.hasAnyMedia()).isTrue()
}
@Test
@@ -74,8 +78,12 @@
underTest.addSelectedUserMediaEntry(userMedia)
assertThat(selectedUserEntries?.get(instanceId)).isEqualTo(userMedia)
+ assertThat(underTest.hasActiveMedia()).isTrue()
+ assertThat(underTest.hasAnyMedia()).isTrue()
assertThat(underTest.removeSelectedUserMediaEntry(instanceId, userMedia)).isTrue()
+ assertThat(underTest.hasActiveMedia()).isFalse()
+ assertThat(underTest.hasAnyMedia()).isFalse()
}
@Test
@@ -144,7 +152,7 @@
underTest.setRecommendation(mediaRecommendation.copy(isActive = false))
assertThat(smartspaceMediaData).isNotEqualTo(mediaRecommendation)
- assertThat(smartspaceMediaData?.isActive).isFalse()
+ assertThat(underTest.isRecommendationActive()).isFalse()
}
@Test
@@ -349,6 +357,14 @@
.inOrder()
}
+ @Test
+ fun hasAnyMedia_noMediaSet_returnsFalse() =
+ testScope.runTest { assertThat(underTest.hasAnyMedia()).isFalse() }
+
+ @Test
+ fun hasActiveMedia_noMediaSet_returnsFalse() =
+ testScope.runTest { assertThat(underTest.hasActiveMedia()).isFalse() }
+
private fun createMediaData(
app: String,
playing: Boolean,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
index 39dbc7e..c62195f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
@@ -76,22 +76,20 @@
testScope.runTest {
val hasActiveMediaOrRecommendation by
collectLastValue(underTest.hasActiveMediaOrRecommendation)
- val hasActiveMedia by collectLastValue(underTest.hasActiveMedia)
- val hasAnyMedia by collectLastValue(underTest.hasAnyMedia)
val userMedia = MediaData(active = true)
mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
assertThat(hasActiveMediaOrRecommendation).isTrue()
- assertThat(hasActiveMedia).isTrue()
- assertThat(hasAnyMedia).isTrue()
+ assertThat(underTest.hasActiveMedia()).isTrue()
+ assertThat(underTest.hasAnyMedia()).isTrue()
mediaFilterRepository.addSelectedUserMediaEntry(userMedia.copy(active = false))
assertThat(hasActiveMediaOrRecommendation).isFalse()
- assertThat(hasActiveMedia).isFalse()
- assertThat(hasAnyMedia).isTrue()
+ assertThat(underTest.hasActiveMedia()).isFalse()
+ assertThat(underTest.hasAnyMedia()).isTrue()
}
@Test
@@ -99,8 +97,6 @@
testScope.runTest {
val hasActiveMediaOrRecommendation by
collectLastValue(underTest.hasActiveMediaOrRecommendation)
- val hasActiveMedia by collectLastValue(underTest.hasActiveMedia)
- val hasAnyMedia by collectLastValue(underTest.hasAnyMedia)
val userMedia = MediaData(active = false)
val instanceId = userMedia.instanceId
@@ -109,8 +105,8 @@
mediaFilterRepository.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId))
assertThat(hasActiveMediaOrRecommendation).isFalse()
- assertThat(hasActiveMedia).isFalse()
- assertThat(hasAnyMedia).isTrue()
+ assertThat(underTest.hasActiveMedia()).isFalse()
+ assertThat(underTest.hasAnyMedia()).isTrue()
assertThat(mediaFilterRepository.removeSelectedUserMediaEntry(instanceId, userMedia))
.isTrue()
@@ -119,8 +115,8 @@
)
assertThat(hasActiveMediaOrRecommendation).isFalse()
- assertThat(hasActiveMedia).isFalse()
- assertThat(hasAnyMedia).isFalse()
+ assertThat(underTest.hasActiveMedia()).isFalse()
+ assertThat(underTest.hasAnyMedia()).isFalse()
}
@Test
@@ -147,6 +143,7 @@
mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
mediaFilterRepository.addMediaDataLoadingState(mediaLoadingModel)
+ mediaFilterRepository.setOrderedMedia()
assertThat(hasActiveMediaOrRecommendation).isTrue()
assertThat(hasAnyMediaOrRecommendation).isTrue()
@@ -202,7 +199,7 @@
@Test
fun hasAnyMedia_noMediaSet_returnsFalse() =
- testScope.runTest { assertThat(underTest.hasAnyMedia.value).isFalse() }
+ testScope.runTest { assertThat(underTest.hasAnyMedia()).isFalse() }
@Test
fun hasAnyMediaOrRecommendation_noMediaSet_returnsFalse() =
@@ -210,7 +207,7 @@
@Test
fun hasActiveMedia_noMediaSet_returnsFalse() =
- testScope.runTest { assertThat(underTest.hasActiveMedia.value).isFalse() }
+ testScope.runTest { assertThat(underTest.hasActiveMedia()).isFalse() }
@Test
fun hasActiveMediaOrRecommendation_nothingSet_returnsFalse() =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt
index 6b1794e2..cb4e2d3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt
@@ -30,8 +30,8 @@
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
-import com.android.systemui.scene.domain.interactor.homeSceneFamilyResolver
import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.ui.viewmodel.notificationsShadeSceneViewModel
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
index e87c8ad..899122d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
@@ -35,6 +35,7 @@
import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
import com.android.systemui.screenrecord.RecordingController
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
+import com.android.systemui.screenrecord.data.repository.ScreenRecordRepositoryImpl
import com.android.systemui.statusbar.phone.KeyguardDismissUtil
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
@@ -72,11 +73,18 @@
.thenReturn(dialog)
}
+ private val screenRecordRepository =
+ ScreenRecordRepositoryImpl(
+ bgCoroutineContext = testScope.testScheduler,
+ recordingController = recordingController,
+ )
+
private val underTest =
ScreenRecordTileUserActionInteractor(
context,
testScope.testScheduler,
testScope.testScheduler,
+ screenRecordRepository,
recordingController,
keyguardInteractor,
keyguardDismissUtil,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
index 7ee20e5..5b6fea5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
@@ -41,10 +41,10 @@
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
import com.android.systemui.res.R
-import com.android.systemui.scene.domain.interactor.homeSceneFamilyResolver
import com.android.systemui.scene.domain.interactor.sceneBackInteractor
import com.android.systemui.scene.domain.interactor.sceneContainerStartable
import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.settings.brightness.ui.viewmodel.brightnessMirrorViewModel
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelTest.kt
index f28ddeb..ac67ac8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelTest.kt
@@ -30,8 +30,8 @@
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
-import com.android.systemui.scene.domain.interactor.homeSceneFamilyResolver
import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.ui.viewmodel.quickSettingsShadeSceneViewModel
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 f8a62cb..4d5d22c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -58,9 +58,9 @@
import com.android.systemui.qs.footerActionsController
import com.android.systemui.qs.footerActionsViewModelFactory
import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
-import com.android.systemui.scene.domain.interactor.homeSceneFamilyResolver
import com.android.systemui.scene.domain.interactor.sceneContainerStartable
import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
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 881ce41..ec7150b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
@@ -21,16 +21,20 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.data.repository.Idle
+import com.android.systemui.scene.data.repository.Transition
import com.android.systemui.scene.data.repository.sceneContainerRepository
+import com.android.systemui.scene.data.repository.setSceneTransition
+import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
import com.android.systemui.scene.sceneContainerConfig
import com.android.systemui.scene.sceneKeys
-import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
@@ -38,6 +42,7 @@
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.test.runCurrent
@@ -54,48 +59,27 @@
private val testScope = kosmos.testScope
private val fakeSceneDataSource = kosmos.fakeSceneDataSource
- private lateinit var underTest: SceneInteractor
+ private val underTest = kosmos.sceneInteractor
@Test
fun allSceneKeys() {
- underTest = kosmos.sceneInteractor
assertThat(underTest.allSceneKeys()).isEqualTo(kosmos.sceneKeys)
}
@Test
fun changeScene_toUnknownScene_doesNothing() =
testScope.runTest {
- val sceneKeys =
- listOf(
- Scenes.QuickSettings,
- Scenes.Shade,
- Scenes.Lockscreen,
- Scenes.Gone,
- Scenes.Communal,
- )
- val navigationDistances =
- mapOf(
- Scenes.Gone to 0,
- Scenes.Lockscreen to 0,
- Scenes.Communal to 1,
- Scenes.Shade to 2,
- Scenes.QuickSettings to 3,
- )
- kosmos.sceneContainerConfig =
- SceneContainerConfig(sceneKeys, Scenes.Lockscreen, navigationDistances)
- underTest = kosmos.sceneInteractor
val currentScene by collectLastValue(underTest.currentScene)
+ val unknownScene = SceneKey("UNKNOWN")
val previousScene = currentScene
- assertThat(previousScene).isNotEqualTo(Scenes.Bouncer)
- underTest.changeScene(Scenes.Bouncer, "reason")
+ assertThat(previousScene).isNotEqualTo(unknownScene)
+ underTest.changeScene(unknownScene, "reason")
assertThat(currentScene).isEqualTo(previousScene)
}
@Test
fun changeScene() =
testScope.runTest {
- underTest = kosmos.sceneInteractor
-
val currentScene by collectLastValue(underTest.currentScene)
assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
@@ -106,8 +90,6 @@
@Test
fun changeScene_toGoneWhenUnl_doesNotThrow() =
testScope.runTest {
- underTest = kosmos.sceneInteractor
-
val currentScene by collectLastValue(underTest.currentScene)
assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
@@ -122,15 +104,11 @@
@Test(expected = IllegalStateException::class)
fun changeScene_toGoneWhenStillLocked_throws() =
- testScope.runTest {
- underTest = kosmos.sceneInteractor
- underTest.changeScene(Scenes.Gone, "reason")
- }
+ testScope.runTest { underTest.changeScene(Scenes.Gone, "reason") }
@Test
fun changeScene_toGoneWhenTransitionToLockedFromGone() =
testScope.runTest {
- underTest = kosmos.sceneInteractor
val currentScene by collectLastValue(underTest.currentScene)
val transitionTo by collectLastValue(underTest.transitioningTo)
kosmos.sceneContainerRepository.setTransitionState(
@@ -155,7 +133,6 @@
@Test
fun changeScene_toHomeSceneFamily() =
testScope.runTest {
- underTest = kosmos.sceneInteractor
val currentScene by collectLastValue(underTest.currentScene)
underTest.changeScene(SceneFamilies.Home, "reason")
@@ -167,37 +144,17 @@
@Test
fun snapToScene_toUnknownScene_doesNothing() =
testScope.runTest {
- val sceneKeys =
- listOf(
- Scenes.QuickSettings,
- Scenes.Shade,
- Scenes.Lockscreen,
- Scenes.Gone,
- Scenes.Communal,
- )
- val navigationDistances =
- mapOf(
- Scenes.Gone to 0,
- Scenes.Lockscreen to 0,
- Scenes.Communal to 1,
- Scenes.Shade to 2,
- Scenes.QuickSettings to 3,
- )
- kosmos.sceneContainerConfig =
- SceneContainerConfig(sceneKeys, Scenes.Lockscreen, navigationDistances)
- underTest = kosmos.sceneInteractor
val currentScene by collectLastValue(underTest.currentScene)
val previousScene = currentScene
- assertThat(previousScene).isNotEqualTo(Scenes.Bouncer)
- underTest.snapToScene(Scenes.Bouncer, "reason")
+ val unknownScene = SceneKey("UNKNOWN")
+ assertThat(previousScene).isNotEqualTo(unknownScene)
+ underTest.snapToScene(unknownScene, "reason")
assertThat(currentScene).isEqualTo(previousScene)
}
@Test
fun snapToScene() =
testScope.runTest {
- underTest = kosmos.sceneInteractor
-
val currentScene by collectLastValue(underTest.currentScene)
assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
@@ -208,8 +165,6 @@
@Test
fun snapToScene_toGoneWhenUnl_doesNotThrow() =
testScope.runTest {
- underTest = kosmos.sceneInteractor
-
val currentScene by collectLastValue(underTest.currentScene)
assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
@@ -224,15 +179,11 @@
@Test(expected = IllegalStateException::class)
fun snapToScene_toGoneWhenStillLocked_throws() =
- testScope.runTest {
- underTest = kosmos.sceneInteractor
- underTest.snapToScene(Scenes.Gone, "reason")
- }
+ testScope.runTest { underTest.snapToScene(Scenes.Gone, "reason") }
@Test
fun snapToScene_toHomeSceneFamily() =
testScope.runTest {
- underTest = kosmos.sceneInteractor
val currentScene by collectLastValue(underTest.currentScene)
underTest.snapToScene(SceneFamilies.Home, "reason")
@@ -244,7 +195,6 @@
@Test
fun sceneChanged_inDataSource() =
testScope.runTest {
- underTest = kosmos.sceneInteractor
val currentScene by collectLastValue(underTest.currentScene)
assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
@@ -256,14 +206,14 @@
@Test
fun transitionState() =
testScope.runTest {
- underTest = kosmos.sceneInteractor
- val underTest = kosmos.sceneContainerRepository
+ val sceneContainerRepository = kosmos.sceneContainerRepository
val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Idle(Scenes.Lockscreen)
)
- underTest.setTransitionState(transitionState)
- val reflectedTransitionState by collectLastValue(underTest.transitionState)
+ sceneContainerRepository.setTransitionState(transitionState)
+ val reflectedTransitionState by
+ collectLastValue(sceneContainerRepository.transitionState)
assertThat(reflectedTransitionState).isEqualTo(transitionState.value)
val progress = MutableStateFlow(1f)
@@ -284,7 +234,7 @@
progress.value = 0.9f
assertThat(reflectedTransitionState).isEqualTo(transitionState.value)
- underTest.setTransitionState(null)
+ sceneContainerRepository.setTransitionState(null)
assertThat(reflectedTransitionState)
.isEqualTo(
ObservableTransitionState.Idle(kosmos.sceneContainerConfig.initialSceneKey)
@@ -294,7 +244,6 @@
@Test
fun transitioningTo() =
testScope.runTest {
- underTest = kosmos.sceneInteractor
val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Idle(underTest.currentScene.value)
@@ -332,7 +281,6 @@
@Test
fun isTransitionUserInputOngoing_idle_false() =
testScope.runTest {
- underTest = kosmos.sceneInteractor
val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Idle(Scenes.Shade)
@@ -347,7 +295,6 @@
@Test
fun isTransitionUserInputOngoing_transition_true() =
testScope.runTest {
- underTest = kosmos.sceneInteractor
val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Transition(
@@ -369,7 +316,6 @@
@Test
fun isTransitionUserInputOngoing_updateMidTransition_false() =
testScope.runTest {
- underTest = kosmos.sceneInteractor
val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Transition(
@@ -403,7 +349,6 @@
@Test
fun isTransitionUserInputOngoing_updateOnIdle_false() =
testScope.runTest {
- underTest = kosmos.sceneInteractor
val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Transition(
@@ -429,7 +374,6 @@
@Test
fun isVisible() =
testScope.runTest {
- underTest = kosmos.sceneInteractor
val isVisible by collectLastValue(underTest.isVisible)
assertThat(isVisible).isTrue()
@@ -443,7 +387,6 @@
@Test
fun isVisible_duringRemoteUserInteraction_forcedVisible() =
testScope.runTest {
- underTest = kosmos.sceneInteractor
underTest.setVisible(false, "reason")
val isVisible by collectLastValue(underTest.isVisible)
assertThat(isVisible).isFalse()
@@ -458,16 +401,53 @@
@Test
fun resolveSceneFamily_home() =
testScope.runTest {
- underTest = kosmos.sceneInteractor
- assertThat(underTest.resolveSceneFamily(SceneFamilies.Home))
- .isEqualTo(kosmos.homeSceneFamilyResolver.resolvedScene)
+ assertThat(underTest.resolveSceneFamily(SceneFamilies.Home).first())
+ .isEqualTo(kosmos.homeSceneFamilyResolver.resolvedScene.value)
}
@Test
fun resolveSceneFamily_nonFamily() =
testScope.runTest {
- underTest = kosmos.sceneInteractor
val resolved = underTest.resolveSceneFamily(Scenes.Gone).toList()
assertThat(resolved).containsExactly(Scenes.Gone).inOrder()
}
+
+ @Test
+ fun transitionValue_test_idle() =
+ testScope.runTest {
+ val transitionValue by collectLastValue(underTest.transitionProgress(Scenes.Gone))
+
+ kosmos.setSceneTransition(Idle(Scenes.Gone))
+ assertThat(transitionValue).isEqualTo(1f)
+
+ kosmos.setSceneTransition(Idle(Scenes.Lockscreen))
+ assertThat(transitionValue).isEqualTo(0f)
+ }
+
+ @Test
+ fun transitionValue_test_transitions() =
+ testScope.runTest {
+ val transitionValue by collectLastValue(underTest.transitionProgress(Scenes.Gone))
+ val progress = MutableStateFlow(0f)
+
+ kosmos.setSceneTransition(
+ Transition(from = Scenes.Lockscreen, to = Scenes.Gone, progress = progress)
+ )
+ assertThat(transitionValue).isEqualTo(0f)
+
+ progress.value = 0.4f
+ assertThat(transitionValue).isEqualTo(0.4f)
+
+ kosmos.setSceneTransition(
+ Transition(from = Scenes.Gone, to = Scenes.Lockscreen, progress = progress)
+ )
+ progress.value = 0.7f
+ assertThat(transitionValue).isEqualTo(0.3f)
+
+ kosmos.setSceneTransition(
+ Transition(from = Scenes.Lockscreen, to = Scenes.Shade, progress = progress)
+ )
+ progress.value = 0.9f
+ assertThat(transitionValue).isEqualTo(0f)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index ac66e66..e40c8ee 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-@file:OptIn(ExperimentalCoroutinesApi::class, ExperimentalCoroutinesApi::class)
+@file:OptIn(ExperimentalCoroutinesApi::class)
package com.android.systemui.scene.domain.startable
@@ -395,6 +395,7 @@
)
assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
underTest.start()
+ runCurrent()
kosmos.fakePowerRepository.updateWakefulness(
rawState = WakefulnessState.STARTING_TO_SLEEP,
@@ -1285,6 +1286,42 @@
}
@Test
+ fun switchToGone_whenSurfaceBehindLockscreenVisibleMidTransition() =
+ testScope.runTest {
+ val currentScene by collectLastValue(sceneInteractor.currentScene)
+ val transitionStateFlow =
+ prepareState(
+ authenticationMethod = AuthenticationMethodModel.None,
+ )
+ underTest.start()
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ // Swipe to Gone, more than halfway
+ transitionStateFlow.value =
+ ObservableTransitionState.Transition(
+ fromScene = Scenes.Lockscreen,
+ toScene = Scenes.Gone,
+ currentScene = flowOf(Scenes.Gone),
+ progress = flowOf(0.51f),
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(true),
+ )
+ runCurrent()
+ // Lift finger
+ transitionStateFlow.value =
+ ObservableTransitionState.Transition(
+ fromScene = Scenes.Lockscreen,
+ toScene = Scenes.Gone,
+ currentScene = flowOf(Scenes.Gone),
+ progress = flowOf(0.51f),
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(false),
+ )
+ runCurrent()
+
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
fun switchToGone_extendUnlock() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt
index 3a5ff00..fa4da42 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt
@@ -29,8 +29,8 @@
import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
-import com.android.systemui.scene.domain.interactor.homeSceneFamilyResolver
import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shared.recents.utilities.Utilities
import com.android.systemui.testKosmos
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
index f89f18a..3ded8a3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
@@ -6,15 +6,27 @@
import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.activityStarter
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.fakeMobileIconsInteractor
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.argThat
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -24,12 +36,16 @@
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
+@EnableSceneContainer
class ShadeHeaderViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val mobileIconsInteractor = kosmos.fakeMobileIconsInteractor
+ private val sceneInteractor = kosmos.sceneInteractor
+ private val deviceEntryInteractor = kosmos.deviceEntryInteractor
private val underTest: ShadeHeaderViewModel = kosmos.shadeHeaderViewModel
@@ -77,6 +93,30 @@
)
}
+ @Test
+ fun onSystemIconContainerClicked_locked_collapsesShadeToLockscreen() =
+ testScope.runTest {
+ setDeviceEntered(false)
+ setScene(Scenes.Shade)
+
+ underTest.onSystemIconContainerClicked()
+ runCurrent()
+
+ assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Lockscreen)
+ }
+
+ @Test
+ fun onSystemIconContainerClicked_unlocked_collapsesShadeToGone() =
+ testScope.runTest {
+ setDeviceEntered(true)
+ setScene(Scenes.Shade)
+
+ underTest.onSystemIconContainerClicked()
+ runCurrent()
+
+ assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Gone)
+ }
+
companion object {
private val SUB_1 =
SubscriptionModel(
@@ -93,6 +133,32 @@
profileClass = PROFILE_CLASS_UNSET,
)
}
+
+ private fun setScene(key: SceneKey) {
+ sceneInteractor.changeScene(key, "test")
+ sceneInteractor.setTransitionState(
+ MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key))
+ )
+ testScope.runCurrent()
+ }
+
+ private fun TestScope.setDeviceEntered(isEntered: Boolean) {
+ if (isEntered) {
+ // Unlock the device marking the device has entered.
+ kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+ SuccessFingerprintAuthenticationStatus(0, true)
+ )
+ runCurrent()
+ }
+ setScene(
+ if (isEntered) {
+ Scenes.Gone
+ } else {
+ Scenes.Lockscreen
+ }
+ )
+ assertThat(deviceEntryInteractor.isDeviceEntered.value).isEqualTo(isEntered)
+ }
}
private class IntentMatcherAction(private val action: String) : ArgumentMatcher<Intent> {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
index f88d102..c53cdf8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
@@ -19,6 +19,8 @@
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.SwipeDirection
import com.android.systemui.SysuiTestCase
@@ -27,6 +29,7 @@
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
@@ -39,8 +42,8 @@
import com.android.systemui.qs.footerActionsViewModelFactory
import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
import com.android.systemui.res.R
-import com.android.systemui.scene.domain.interactor.homeSceneFamilyResolver
import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
@@ -57,7 +60,9 @@
import com.google.common.truth.Truth.assertThat
import java.util.Locale
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -126,9 +131,7 @@
kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin
)
- kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
- SuccessFingerprintAuthenticationStatus(0, true)
- )
+ setDeviceEntered(true)
assertThat(destinationScenes?.get(Swipe(SwipeDirection.Up))?.toScene)
.isEqualTo(SceneFamilies.Home)
@@ -196,9 +199,7 @@
kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
AuthenticationMethodModel.Pin
)
- kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
- SuccessFingerprintAuthenticationStatus(0, true)
- )
+ setDeviceEntered(true)
runCurrent()
assertThat(isClickable).isFalse()
@@ -345,6 +346,32 @@
return maxTranslation
}
+ private fun TestScope.setDeviceEntered(isEntered: Boolean) {
+ if (isEntered) {
+ // Unlock the device marking the device has entered.
+ kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+ SuccessFingerprintAuthenticationStatus(0, true)
+ )
+ runCurrent()
+ }
+ setScene(
+ if (isEntered) {
+ Scenes.Gone
+ } else {
+ Scenes.Lockscreen
+ }
+ )
+ assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isEqualTo(isEntered)
+ }
+
+ private fun TestScope.setScene(key: SceneKey) {
+ sceneInteractor.changeScene(key, "test")
+ sceneInteractor.setTransitionState(
+ MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key))
+ )
+ runCurrent()
+ }
+
private data class Translations(
val start: Float,
val end: Float,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
index c4506f2..ca4434d2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
@@ -121,10 +121,13 @@
sceneInteractor.setTransitionState(transitionState)
val expandFraction by collectLastValue(scrollViewModel.expandFraction)
assertThat(expandFraction).isEqualTo(0f)
+
+ fakeSceneDataSource.changeScene(toScene = Scenes.Gone)
val isScrollable by collectLastValue(scrollViewModel.isScrollable)
assertThat(isScrollable).isFalse()
fakeSceneDataSource.pause()
+
sceneInteractor.changeScene(Scenes.Shade, "reason")
val transitionProgress = MutableStateFlow(0f)
transitionState.value =
@@ -159,8 +162,10 @@
sceneInteractor.setTransitionState(transitionState)
val expandFraction by collectLastValue(scrollViewModel.expandFraction)
assertThat(expandFraction).isEqualTo(1f)
+
+ fakeSceneDataSource.changeScene(toScene = Scenes.Lockscreen)
val isScrollable by collectLastValue(scrollViewModel.isScrollable)
- assertThat(isScrollable).isFalse()
+ assertThat(isScrollable).isTrue()
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index c35c165..497484f90 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -42,6 +42,7 @@
import com.android.systemui.keyguard.shared.model.BurnInModel
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
@@ -842,6 +843,30 @@
}
@Test
+ @DisableSceneContainer
+ fun updateBounds_fromGone_withoutTransitions() =
+ testScope.runTest {
+ // Start step is already at 1.0
+ val runningStep = TransitionStep(GONE, AOD, 1.0f, TransitionState.RUNNING)
+ val finishStep = TransitionStep(GONE, AOD, 1.0f, TransitionState.FINISHED)
+
+ val bounds by collectLastValue(underTest.bounds)
+ val top = 123f
+ val bottom = 456f
+
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(runningStep)
+ runCurrent()
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(finishStep)
+ runCurrent()
+ keyguardRootViewModel.onNotificationContainerBoundsChanged(top, bottom)
+ runCurrent()
+
+ assertThat(bounds).isEqualTo(
+ NotificationContainerBounds(top = top, bottom = bottom)
+ )
+ }
+
+ @Test
fun alphaOnFullQsExpansion() =
testScope.runTest {
val viewState = ViewStateAccessor()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
index 88bef91..206b39c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
@@ -627,7 +627,7 @@
hum.onEntryAdded(entryToPin);
assertEquals(2, mUiEventLoggerFake.numLogs());
- assertEquals(AvalancheController.ThrottleEvent.SHOWN.getId(),
+ assertEquals(AvalancheController.ThrottleEvent.AVALANCHE_THROTTLING_HUN_SHOWN.getId(),
mUiEventLoggerFake.eventId(0));
assertEquals(BaseHeadsUpManager.NotificationPeekEvent.NOTIFICATION_PEEK.getId(),
mUiEventLoggerFake.eventId(1));
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt
index 10a4eb7..7385a47 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt
@@ -71,6 +71,7 @@
addOverride(R.drawable.ic_headphone, testIcon)
addOverride(R.drawable.ic_smartphone, testIcon)
addOverride(R.drawable.ic_media_speaker_device, testIcon)
+ addOverride(R.drawable.ic_media_tablet, testIcon)
addOverride(com.android.internal.R.drawable.ic_bt_hearing_aid, testIcon)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt
index 8921a23..0f56d0b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt
@@ -65,6 +65,7 @@
with(context.orCreateTestableResources) {
addOverride(R.drawable.ic_smartphone, testIcon)
+ addOverride(R.drawable.ic_media_tablet, testIcon)
addOverride(R.string.media_transfer_this_device_name_tv, builtInDeviceName)
addOverride(R.string.media_transfer_this_device_name_tablet, builtInDeviceName)
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
index 83658d3..9ad4012 100644
--- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -134,6 +134,12 @@
default void setScreenOn(boolean screenOn) {}
/**
+ * Sets a delegate to handle clock event registration. Should be called immediately after
+ * the view is created.
+ */
+ default void setTimeChangedDelegate(TimeChangedDelegate delegate) {}
+
+ /**
* Set if dozing is true or false
*/
default void setDozing(boolean dozing) {}
@@ -228,4 +234,13 @@
/** Start the PendingIntent */
void startPendingIntent(View v, PendingIntent pi, boolean showOnLockscreen);
}
+
+ /** Interface for delegating time updates */
+ interface TimeChangedDelegate {
+ /** Register the callback to be called when time is updated **/
+ void register(Runnable callback);
+
+ /** Unegister the callback **/
+ void unregister();
+ }
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
index de65931..7cf56aa 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
@@ -58,6 +58,19 @@
@Nullable ActivityTransitionAnimator.Controller animationController);
/**
+ * Similar to {@link #startPendingIntentMaybeDismissingKeyguard(PendingIntent, Runnable,
+ * ActivityTransitionAnimator.Controller)} but will always not dismiss the keyguard when
+ * launching activities. This should be avoided and other alternatives should be used.
+ */
+ void startPendingIntentWithoutDismissing(
+ PendingIntent intent,
+ boolean dismissShade,
+ Runnable intentSentUiThreadCallback,
+ @Nullable ActivityTransitionAnimator.Controller animationController,
+ @Nullable Intent fillInIntent,
+ @Nullable Bundle extraOptions);
+
+ /**
* Similar to {@link #startPendingIntentDismissingKeyguard}, except that it supports launching
* activities on top of the keyguard. If the activity supports {@code showOverLockscreen}, it
* will show over keyguard without first dimissing it. If it doesn't support it, calling this
diff --git a/packages/SystemUI/res/drawable/android15_patch_adaptive.xml b/packages/SystemUI/res/drawable/android15_patch_adaptive.xml
new file mode 100644
index 0000000..d949200
--- /dev/null
+++ b/packages/SystemUI/res/drawable/android15_patch_adaptive.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/android15_patch_adaptive_background"/>
+ <foreground android:drawable="@drawable/android15_patch_adaptive_foreground"/>
+ <monochrome android:drawable="@drawable/android15_patch_monochrome"/>
+</adaptive-icon>
diff --git a/packages/SystemUI/res/drawable/android15_patch_adaptive_background.xml b/packages/SystemUI/res/drawable/android15_patch_adaptive_background.xml
new file mode 100644
index 0000000..d4850d3
--- /dev/null
+++ b/packages/SystemUI/res/drawable/android15_patch_adaptive_background.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108">
+ <!-- space (themed version) -->
+ <path
+ android:pathData="M0,0h108v108h-108z"
+ android:fillColor="@android:color/system_neutral1_800"/>
+ <!-- stars (themed version) -->
+ <group>
+ <path
+ android:pathData="M32,34 h1v1h-1z"
+ android:fillColor="@android:color/system_accent3_200"/>
+ <path
+ android:pathData="M33,61 h1v1h-1z"
+ android:fillColor="@android:color/system_accent3_200"/>
+ <path
+ android:pathData="M71,34 h1v1h-1z"
+ android:fillColor="@android:color/system_accent3_200"/>
+ <path
+ android:pathData="M62,56 h1v1h-1z"
+ android:fillColor="@android:color/system_accent3_200"/>
+ <path
+ android:pathData="M68,47 h1v1h-1z"
+ android:fillColor="@android:color/system_accent3_200"/>
+ <path
+ android:pathData="M72,55 h1v1h-1z"
+ android:fillColor="@android:color/system_accent3_200"/>
+ <path
+ android:pathData="M39,36 h1v1h-1z"
+ android:fillColor="@android:color/system_accent3_200"/>
+
+ <path
+ android:pathData="M72,49 h2v2h-2z"
+ android:fillColor="@android:color/system_accent3_200"/>
+ <path
+ android:pathData="M46,53 h2v2h-2z"
+ android:fillColor="@android:color/system_accent3_200"/>
+ <path
+ android:pathData="M32,45 h2v2h-2z"
+ android:fillColor="@android:color/system_accent3_200"/>
+ <path
+ android:pathData="M43,37 h2v2h-2z"
+ android:fillColor="@android:color/system_accent3_200"/>
+ <path
+ android:pathData="M78,51 h2v2h-2z"
+ android:fillColor="@android:color/system_accent3_200"/>
+ <path
+ android:pathData="M34,51 h2v2h-2z"
+ android:fillColor="@android:color/system_accent3_200"/>
+ <path
+ android:pathData="M76,41 h2v2h-2z"
+ android:fillColor="@android:color/system_accent3_200"/>
+ <path
+ android:pathData="M39,46 h2v2h-2z"
+ android:fillColor="@android:color/system_accent3_200"/>
+ </group>
+</vector>
diff --git a/packages/SystemUI/res/drawable/android15_patch_adaptive_foreground.xml b/packages/SystemUI/res/drawable/android15_patch_adaptive_foreground.xml
new file mode 100644
index 0000000..34f6ee0
--- /dev/null
+++ b/packages/SystemUI/res/drawable/android15_patch_adaptive_foreground.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108">
+
+ <!-- zoomies (themed version) -->
+ <group>
+ <path
+ android:pathData="M53,42C52.21,50.58 46.46,68.95 32.11,74.63C19.22,79.75 5.77,82.32 1.19,83.19C0.68,83.29 0.28,83.36 0,83.42V108H54H108V83.42C107.72,83.36 107.32,83.29 106.81,83.19C102.23,82.32 88.78,79.75 75.89,74.63C61.54,68.95 55.79,50.58 55,42H54H53Z"
+ android:fillColor="@android:color/system_accent1_100"
+ android:fillType="evenOdd"/>
+ <path
+ android:pathData="M53.25,42C52.88,50.53 50.44,69.01 43.68,74.67C36.91,80.33 32.65,82.86 31.37,83.41L54,102.87L76.63,83.41C75.35,82.86 71.09,80.33 64.32,74.67C57.56,69.01 55.12,50.53 54.75,42H54H53.25Z"
+ android:fillColor="#ffffff"
+ android:fillType="evenOdd"/>
+ <path
+ android:pathData="M54,42m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0"
+ android:fillColor="#ffffff"/>
+ </group>
+ <group>
+ <!-- head! it's like sputnik -->
+ <path
+ android:pathData="M54,94.25m-26.25,0a26.25,26.25 0,1 1,52.5 0a26.25,26.25 0,1 1,-52.5 0"
+ android:fillColor="#34A853"/>
+ <!-- ant -->
+ <path
+ android:pathData="M38,63.5L44.5,74.5"
+ android:strokeWidth="5"
+ android:fillColor="#00000000"
+ android:strokeColor="#34A853"
+ android:strokeLineCap="round"/>
+ <path
+ android:pathData="M70,63.5L63.5,74.5"
+ android:strokeWidth="5"
+ android:fillColor="#00000000"
+ android:strokeColor="#34A853"
+ android:strokeLineCap="round"/>
+ </group>
+ <!-- spaceship -->
+ <path
+ android:pathData="M54,34C52.34,34 51,35.29 51,36.88V40.44C51,40.75 51.25,41 51.56,41C51.87,41 52.13,40.75 52.13,40.44V39.48C52.13,38.87 52.63,38.37 53.25,38.37H54.75C55.37,38.37 55.87,38.87 55.87,39.48V40.44C55.87,40.75 56.13,41 56.44,41C56.75,41 57,40.75 57,40.44V36.88C57,35.29 55.66,34 54,34H54Z"
+ android:fillColor="#E9F3EB"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/android15_patch_monochrome.xml b/packages/SystemUI/res/drawable/android15_patch_monochrome.xml
new file mode 100644
index 0000000..a91cc86
--- /dev/null
+++ b/packages/SystemUI/res/drawable/android15_patch_monochrome.xml
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108">
+ <group>
+ <path
+ android:pathData="
+ M54,94.25
+ m-26.25,0
+ a26.25,26.25 0,1 1,52.5 0
+ a26.25,26.25 0,1 1,-52.5 0
+ "
+ android:fillColor="#000000"/>
+ <path
+ android:pathData="M38,63.5L44.5,74.5"
+ android:strokeWidth="5"
+ android:fillColor="#00000000"
+ android:strokeColor="#000000"
+ android:strokeLineCap="round"/>
+ <path
+ android:pathData="M70,63.5L63.5,74.5"
+ android:strokeWidth="5"
+ android:fillColor="#00000000"
+ android:strokeColor="#000000"
+ android:strokeLineCap="round"/>
+
+ <path
+ android:pathData="
+ M54,34
+ C52.34,34 51,35.29 51,36.88
+ V40.44
+ C51,40.75 51.25,41 51.56,41
+ C51.87,41 52.13,40.75 52.13,40.44
+ V39.48
+ C52.13,38.87 52.63,38.37 53.25,38.37
+ H54.75
+ C55.37,38.37 55.87,38.87 55.87,39.48
+ V40.44
+ C55.87,40.75 56.13,41 56.44,41
+ C56.75,41 57,40.75 57,40.44
+ V36.88
+ C57,35.29 55.66,34 54,34
+ H54
+ Z
+ "
+ android:fillColor="#34A853"/>
+ <path
+ android:strokeWidth="1"
+ android:pathData="M54,40V67"
+ android:fillColor="#00000000"
+ android:strokeColor="#40FFFFFF"
+ />
+
+ <path
+ android:pathData="M32,34 h1v1h-1z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M33,61 h1v1h-1z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M71,34 h1v1h-1z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M62,56 h1v1h-1z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M68,47 h1v1h-1z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M72,55 h1v1h-1z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M39,36 h1v1h-1z"
+ android:fillColor="#ffffff"/>
+
+ <path
+ android:pathData="M72,49 h2v2h-2z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M46,53 h2v2h-2z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M32,45 h2v2h-2z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M43,37 h2v2h-2z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M78,51 h2v2h-2z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M34,51 h2v2h-2z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M76,41 h2v2h-2z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M39,46 h2v2h-2z"
+ android:fillColor="#ffffff"/>
+
+ </group>
+</vector>
diff --git a/core/res/res/drawable/immersive_cling_bg_circ.xml b/packages/SystemUI/res/drawable/ic_bt_le_audio_sharing_18dp.xml
similarity index 60%
copy from core/res/res/drawable/immersive_cling_bg_circ.xml
copy to packages/SystemUI/res/drawable/ic_bt_le_audio_sharing_18dp.xml
index 4731bbd..dd3d9e3 100644
--- a/core/res/res/drawable/immersive_cling_bg_circ.xml
+++ b/packages/SystemUI/res/drawable/ic_bt_le_audio_sharing_18dp.xml
@@ -1,6 +1,6 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
- ~ Copyright (C) 2015 The Android Open Source Project
+ ~ Copyright (C) 2024 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -12,15 +12,11 @@
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
- ~ limitations under the License
+ ~ limitations under the License.
-->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="oval" >
-
- <solid android:color="@color/white" />
-
- <size
- android:height="56dp"
- android:width="56dp" />
-
-</shape>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
+ <item
+ android:drawable="@drawable/ic_bt_le_audio_sharing"
+ android:width="18dp"
+ android:height="18dp" />
+</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_swipe_down.xml b/packages/SystemUI/res/drawable/ic_swipe_down.xml
new file mode 100644
index 0000000..15712d6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_swipe_down.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M180,600L40,460L82,418L152,488Q146,461 143,434Q140,407 140,380Q140,298 167,221Q194,144 245,80L288,123Q245,179 222.5,244.5Q200,310 200,380Q200,406 203,431.5Q206,457 213,482L278,418L320,460L180,600ZM658,833Q635,841 611.5,840.5Q588,840 566,829L304,707L322,667Q332,647 350,634.5Q368,622 390,620L458,615L346,308Q340,292 347,277.5Q354,263 370,257Q386,251 400.5,258Q415,265 421,281L569,688L469,695L600,756Q607,759 615,759.5Q623,760 630,758L787,701Q818,690 832,659.5Q846,629 835,598L780,448Q774,432 781,417.5Q788,403 804,397Q820,391 834.5,398Q849,405 855,421L910,571Q933,634 905.5,693.5Q878,753 815,776L658,833ZM568,568L514,417Q508,401 515,386.5Q522,372 538,366Q554,360 568.5,367Q583,374 589,390L644,540L568,568ZM681,527L640,414Q634,398 641,383.5Q648,369 664,363Q680,357 694.5,364Q709,371 715,387L756,499L681,527ZM689,605L689,605L689,605Q689,605 689,605Q689,605 689,605L689,605Q689,605 689,605Q689,605 689,605L689,605L689,605Z"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/immersive_cling_bg_circ.xml b/packages/SystemUI/res/drawable/immersive_cling_bg.xml
similarity index 67%
copy from core/res/res/drawable/immersive_cling_bg_circ.xml
copy to packages/SystemUI/res/drawable/immersive_cling_bg.xml
index 4731bbd..de29c32 100644
--- a/core/res/res/drawable/immersive_cling_bg_circ.xml
+++ b/packages/SystemUI/res/drawable/immersive_cling_bg.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2015 The Android Open Source Project
+ ~ Copyright (C) 2024 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -15,12 +15,10 @@
~ limitations under the License
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="oval" >
-
- <solid android:color="@color/white" />
-
- <size
- android:height="56dp"
- android:width="56dp" />
-
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+ <corners
+ android:bottomLeftRadius="28dp"
+ android:bottomRightRadius="28dp"/>
+ <solid android:color="?androidprv:attr/materialColorSurfaceContainer" />
</shape>
diff --git a/packages/SystemUI/res/drawable/immersive_cling_bg_circ.xml b/packages/SystemUI/res/drawable/immersive_cling_bg_circ.xml
deleted file mode 100644
index 32e88ab..0000000
--- a/packages/SystemUI/res/drawable/immersive_cling_bg_circ.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?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"
- android:shape="oval" >
-
- <solid android:color="?android:attr/colorBackground" />
-
- <size
- android:height="56dp"
- android:width="56dp" />
-
-</shape>
diff --git a/packages/SystemUI/res/drawable/immersive_cling_btn_bg.xml b/packages/SystemUI/res/drawable/immersive_cling_btn_bg.xml
new file mode 100644
index 0000000..df49e38
--- /dev/null
+++ b/packages/SystemUI/res/drawable/immersive_cling_btn_bg.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android">
+ <ripple android:color="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <solid android:color="@android:color/white" />
+ <corners android:radius="28dp" />
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="rectangle">
+ <corners android:radius="28dp" />
+ <solid android:color="?android:attr/colorAccent" />
+ </shape>
+ </item>
+ </ripple>
+</inset>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/immersive_cling_light_bg_circ.xml b/packages/SystemUI/res/drawable/immersive_cling_light_bg_circ.xml
deleted file mode 100644
index 12c3e23..0000000
--- a/packages/SystemUI/res/drawable/immersive_cling_light_bg_circ.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?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"
- android:shape="oval" >
-
- <solid android:color="?android:attr/colorBackground" />
-
- <size
- android:height="76dp"
- android:width="76dp" />
-
-</shape>
diff --git a/packages/SystemUI/res/layout-land/volume_dialog.xml b/packages/SystemUI/res/layout-land/volume_dialog.xml
index 5ce2601..08edf59 100644
--- a/packages/SystemUI/res/layout-land/volume_dialog.xml
+++ b/packages/SystemUI/res/layout-land/volume_dialog.xml
@@ -13,9 +13,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:sysui="http://schemas.android.com/apk/res-auto"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:id="@+id/volume_dialog_container"
android:layout_width="wrap_content"
@@ -97,16 +95,18 @@
android:paddingLeft="@dimen/volume_dialog_ringer_rows_padding"
android:paddingBottom="@dimen/volume_dialog_ringer_rows_padding"
android:paddingRight="@dimen/volume_dialog_ringer_rows_padding">
+
<com.android.keyguard.AlphaOptimizedImageButton
android:id="@+id/settings"
- android:src="@drawable/horizontal_ellipsis"
android:layout_width="@dimen/volume_dialog_tap_target_size"
android:layout_height="@dimen/volume_dialog_tap_target_size"
android:layout_gravity="center"
- android:contentDescription="@string/accessibility_volume_settings"
android:background="@drawable/ripple_drawable_20dp"
- android:tint="?androidprv:attr/colorAccent"
- android:soundEffectsEnabled="false" />
+ android:contentDescription="@string/accessibility_volume_settings"
+ android:scaleType="centerInside"
+ android:soundEffectsEnabled="false"
+ android:src="@drawable/horizontal_ellipsis"
+ android:tint="?androidprv:attr/colorAccent" />
</FrameLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml b/packages/SystemUI/res/layout/ambient_status_bar_view.xml
similarity index 97%
rename from packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
rename to packages/SystemUI/res/layout/ambient_status_bar_view.xml
index ec2edb5..7d765ce 100644
--- a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
+++ b/packages/SystemUI/res/layout/ambient_status_bar_view.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.dreams.DreamOverlayStatusBarView
+<com.android.systemui.ambient.statusbar.ui.AmbientStatusBarView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/dream_overlay_status_bar"
@@ -118,4 +118,4 @@
android:contentDescription="@string/assistant_attention_content_description" />
</LinearLayout>
-</com.android.systemui.dreams.DreamOverlayStatusBarView>
+</com.android.systemui.ambient.statusbar.ui.AmbientStatusBarView>
diff --git a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
index a598007..27b8006 100644
--- a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
+++ b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
@@ -268,6 +268,12 @@
android:ellipsize="end"
android:maxLines="1"
android:text="@string/quick_settings_bluetooth_audio_sharing_button"
+ android:drawableStart="@drawable/ic_bt_le_audio_sharing_18dp"
+ android:drawablePadding="10dp"
+ android:drawableTint="?android:attr/textColorPrimary"
+ app:layout_constrainedWidth="true"
+ app:layout_constraintHorizontal_bias="0"
+ app:layout_constraintEnd_toStartOf="@+id/done_button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/barrier"
diff --git a/packages/SystemUI/res/layout/dream_overlay_container.xml b/packages/SystemUI/res/layout/dream_overlay_container.xml
index 4234fca5..dcd3fa6 100644
--- a/packages/SystemUI/res/layout/dream_overlay_container.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_container.xml
@@ -44,5 +44,5 @@
app:layout_constraintBottom_toBottomOf="parent"
/>
- <include layout="@layout/dream_overlay_status_bar_view" />
+ <include layout="@layout/ambient_status_bar_view" />
</com.android.systemui.dreams.DreamOverlayContainerView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/hearing_devices_preset_dropdown_item.xml b/packages/SystemUI/res/layout/hearing_devices_preset_dropdown_item.xml
index 1d9307b..17c0222 100644
--- a/packages/SystemUI/res/layout/hearing_devices_preset_dropdown_item.xml
+++ b/packages/SystemUI/res/layout/hearing_devices_preset_dropdown_item.xml
@@ -22,4 +22,5 @@
android:minHeight="@dimen/hearing_devices_preset_spinner_height"
android:paddingStart="@dimen/hearing_devices_preset_spinner_text_padding_start"
android:gravity="center_vertical"
+ android:textDirection="locale"
android:ellipsize="end" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/hearing_devices_preset_spinner_selected.xml b/packages/SystemUI/res/layout/hearing_devices_preset_spinner_selected.xml
index 77172ca..d512e7c 100644
--- a/packages/SystemUI/res/layout/hearing_devices_preset_spinner_selected.xml
+++ b/packages/SystemUI/res/layout/hearing_devices_preset_spinner_selected.xml
@@ -32,6 +32,7 @@
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
android:textSize="14sp"
android:gravity="center_vertical"
+ android:textDirection="locale"
android:layout_weight="1" />
<TextView
android:id="@+id/hearing_devices_preset_option_text"
@@ -42,5 +43,6 @@
android:gravity="center_vertical"
android:ellipsize="end"
android:maxLines="1"
+ android:textDirection="locale"
android:layout_weight="1" />
</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/immersive_mode_cling.xml b/packages/SystemUI/res/layout/immersive_mode_cling.xml
index e6529b9..20b7cd3 100644
--- a/packages/SystemUI/res/layout/immersive_mode_cling.xml
+++ b/packages/SystemUI/res/layout/immersive_mode_cling.xml
@@ -14,78 +14,67 @@
limitations under the License.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:theme="@android:style/Theme.DeviceDefault.DayNight"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/immersive_cling_bg"
+ android:gravity="center_vertical"
+ android:padding="24dp">
+
+ <!-- The top margin of this icon can be adjusted to push the content down to prevent overlapping
+ with the display cutout. -->
+ <ImageView
+ android:id="@+id/immersive_cling_icon"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:layout_centerHorizontal="true"
+ android:scaleType="fitXY"
+ android:src="@drawable/ic_swipe_down"
+ android:tint="?android:attr/colorAccent"
+ android:tintMode="src_in" />
+
+ <TextView
+ android:id="@+id/immersive_cling_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:paddingBottom="24dp">
-
- <FrameLayout
- android:id="@+id/immersive_cling_chevron"
- android:layout_width="76dp"
- android:layout_height="76dp"
- android:layout_marginTop="-24dp"
- android:layout_centerHorizontal="true">
-
- <ImageView
- android:id="@+id/immersive_cling_back_bg_light"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="center"
- android:src="@drawable/immersive_cling_light_bg_circ" />
-
- <ImageView
- android:id="@+id/immersive_cling_back_bg"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="center"
- android:src="@drawable/immersive_cling_bg_circ" />
-
- <ImageView
- android:id="@+id/immersive_cling_ic_expand_more"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingTop="8dp"
- android:scaleType="center"
- android:src="@drawable/ic_expand_more_48dp"/>
- </FrameLayout>
+ android:layout_below="@id/immersive_cling_icon"
+ android:layout_marginTop="20dp"
+ android:gravity="center_horizontal"
+ android:text="@string/immersive_cling_title"
+ android:textColor="?androidprv:attr/materialColorOnSurface"
+ android:textSize="24sp"
+ android:fontFamily="google-sans" />
<TextView
- android:id="@+id/immersive_cling_title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/immersive_cling_chevron"
- android:paddingEnd="48dp"
- android:paddingStart="48dp"
- android:paddingTop="40dp"
- android:text="@string/immersive_cling_title"
- android:textColor="?android:attr/textColorPrimaryInverse"
- android:textSize="24sp" />
-
- <TextView
- android:id="@+id/immersive_cling_description"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/immersive_cling_title"
- android:paddingEnd="48dp"
- android:paddingStart="48dp"
- android:paddingTop="12.6dp"
- android:text="@string/immersive_cling_description"
- android:textColor="?android:attr/textColorPrimaryInverse"
- android:textSize="16sp" />
+ android:id="@+id/immersive_cling_description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/immersive_cling_title"
+ android:paddingTop="14dp"
+ android:gravity="center_horizontal"
+ android:text="@string/immersive_cling_description"
+ android:textColor="?androidprv:attr/materialColorOnSurfaceVariant"
+ android:textSize="14sp"
+ android:fontFamily="google-sans" />
<Button
- android:id="@+id/ok"
- style="@android:style/Widget.Material.Button.Borderless"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentEnd="true"
- android:layout_below="@+id/immersive_cling_description"
- android:layout_marginEnd="40dp"
- android:layout_marginTop="18dp"
- android:paddingEnd="8dp"
- android:paddingStart="8dp"
- android:text="@string/immersive_cling_positive"
- android:textColor="?android:attr/textColorPrimaryInverse"
- android:textSize="14sp" />
-
+ android:id="@+id/ok"
+ style="@android:style/Widget.Material.Button.Borderless.Colored"
+ android:background="@drawable/immersive_cling_btn_bg"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_below="@+id/immersive_cling_description"
+ android:layout_marginTop="24dp"
+ android:paddingStart="18dp"
+ android:paddingEnd="18dp"
+ android:minWidth="48dp"
+ android:minHeight="48dp"
+ android:text="@string/immersive_cling_positive"
+ android:textColor="?androidprv:attr/materialColorOnPrimary"
+ android:textAllCaps="false"
+ android:textSize="14sp"
+ android:textFontWeight="500"
+ android:fontFamily="google-sans" />
</RelativeLayout>
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index 221b791..fbb07be 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -62,7 +62,9 @@
<com.android.systemui.keyguard.ui.view.KeyguardRootView
android:id="@id/keyguard_root_view"
android:layout_width="match_parent"
- android:layout_height="match_parent" />
+ android:layout_height="match_parent"
+ android:clipChildren="false"
+ />
<!-- Shared container for the notification stack. Can be positioned by either
the keyguard_root_view or notification_panel -->
diff --git a/packages/SystemUI/res/raw/keep.xml b/packages/SystemUI/res/raw/keep_homecontrols_sq.xml
similarity index 100%
rename from packages/SystemUI/res/raw/keep.xml
rename to packages/SystemUI/res/raw/keep_homecontrols_sq.xml
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index c25d8cb..a7dfdaa 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Skakel dit môre outomaties weer aan"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Kenmerke soos Kitsdeel en Kry My Toestel gebruik Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth sal môreoggend aanskakel"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Oudiodeling"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Deel tans oudio"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batterykrag"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Oudio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Kopstuk"</string>
@@ -361,18 +363,13 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Watter deel van jou toestelervaring is geraak?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Kies soort kwessie"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skermopname"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Werkverrigting"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Gebruikerkoppelvlak"</string>
+ <string name="thermal" msgid="6758074791325414831">"Termies"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Eenhandmodus"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Gehoortoestelle"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktief"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Ontkoppel"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Gehoortoestelle"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Bind nuwe toestel saam"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik om nuwe toestel saam te bind"</string>
@@ -1201,7 +1198,7 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-fi sal vir nou nie outomaties koppel nie"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Sien alles"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ontkoppel Ethernet om netwerke te wissel"</string>
- <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Om toestelervaring te verbeter, kan programme en dienste steeds enige tyd na wi‑fi-netwerke soek, selfs wanneer wi‑fi af is. Jy kan dit in Wi-fi-opsporinginstellings verander. "<annotation id="link">"Verander"</annotation></string>
+ <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Om toestelervaring te verbeter, kan apps en dienste steeds enige tyd na wi‑fi-netwerke soek, selfs wanneer wi‑fi af is. Jy kan dit in Wi-fi-opsporinginstellings verander. "<annotation id="link">"Verander"</annotation></string>
<string name="turn_off_airplane_mode" msgid="8425587763226548579">"Skakel vliegtuigmodus af"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> wil die volgende teël by Kitsinstellings voeg"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Voeg teël by"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"gevou"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"oopgevou"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Stilusbattery <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Koppel jou stilus aan ’n laaier"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Stilus se battery is amper pap"</string>
<string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Soekkortpaaie"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Vou ikoon in"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Vou ikoon uit"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"of"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Sleutelbordlig"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Vlak %1$d van %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Huiskontroles"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index ed6a3e1..396837e 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ነገ እንደገና በራስ-ሰር አስጀምር"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"እንደ ፈጣን ማጋራት እና የእኔን መሣሪያ አግኝ ያሉ ባህሪዎች ብሉቱዝን ይጠቀማሉ"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ብሉቱዝ ነገ ጠዋት ይበራል"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"የድምጽ ማጋራት"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ድምጽን በማጋራት ላይ"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ባትሪ"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ኦዲዮ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ማዳመጫ"</string>
@@ -361,18 +363,13 @@
<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>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"አፈጻጸም"</string>
+ <string name="user_interface" msgid="3712869377953950887">"የተጠቃሚ በይነገፅ"</string>
+ <string name="thermal" msgid="6758074791325414831">"ተርማል"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"የአንድ እጅ ሁነታ"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"የመስሚያ መሣሪያዎች"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ገቢር"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ግንኙነት ተቋርጧል"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"የመስማት ችሎታ መሣሪያ"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"አዲስ መሣሪያ ያጣምሩ"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"አዲስ መሣሪያ ለማጣመር ጠቅ ያድርጉ"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"የታጠፈ"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"የተዘረጋ"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"የብሮስፌ ባትሪ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ብሮስፌዎን ከኃይል መሙያ ጋር ያገናኙ"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"የብሮስፌ ባትሪ ዝቅተኛ ነው"</string>
<string name="video_camera" msgid="7654002575156149298">"የቪድዮ ካሜራ"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"የፍለጋ አቋራጮች"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"መሰብሰቢያ አዶ"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"መዘርጊያ አዶ"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ወይም"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"የቁልፍ ሰሌዳ የጀርባ ብርሃን"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"ደረጃ %1$d ከ %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"የቤት ውስጥ ቁጥጥሮች"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 1502765..254549f 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"تفعيل البلوتوث تلقائيًا مرة أخرى غدًا"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"يُستخدَم البلوتوث في ميزات مثل Quick Share و\"العثور على جهازي\""</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"سيتم تفعيل البلوتوث صباح الغد"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"مشاركة الصوت"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"مشاركة الصوت"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"مستوى طاقة البطارية <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"صوت"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"سماعة الرأس"</string>
@@ -301,11 +303,11 @@
<string name="quick_settings_user_title" msgid="8673045967216204537">"المستخدم"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"الإنترنت"</string>
- <string name="quick_settings_networks_available" msgid="1875138606855420438">"الشبكات متوفرة"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"تتوفّر شبكات"</string>
<string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"الشبكات غير متوفرة"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"لا تتوفر أي شبكة Wi-Fi"</string>
<string name="quick_settings_wifi_secondary_label_transient" msgid="7501659015509357887">"جارٍ التفعيل…"</string>
- <string name="quick_settings_cast_title" msgid="3033553249449938182">"الإرسال"</string>
+ <string name="quick_settings_cast_title" msgid="3033553249449938182">"البث"</string>
<string name="quick_settings_casting" msgid="1435880708719268055">"جارٍ الإرسال"</string>
<string name="quick_settings_cast_device_default_name" msgid="6988469571141331700">"جهاز لا يحمل اسمًا"</string>
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"لا يتوفر أي جهاز"</string>
@@ -366,10 +368,8 @@
<string name="thermal" msgid="6758074791325414831">"الأداء الحراري"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"وضع \"التصفح بيد واحدة\""</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"سماعات الأذن الطبية"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"متّصلة حاليًا"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"غير متّصلة"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"سماعات الأذن الطبية"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"إقران جهاز جديد"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"انقر لإقران جهاز جديد"</string>
@@ -1324,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"اختصارات طلبات البحث"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"رمز التصغير"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"رمز التوسيع"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"أو"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"الإضاءة الخلفية للوحة المفاتيح"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"مستوى الإضاءة: %1$d من %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"إدارة المنزل آليًّا"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 00d673e..dd71023 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"কাইলৈ পুনৰ স্বয়ংক্ৰিয়ভাৱে অন কৰক"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share আৰু Find My Deviceৰ দৰে সুবিধাসমূহে ব্লুটুথ ব্যৱহাৰ কৰে"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"কাইলৈ পুৱা ব্লুটুথ অন হ’ব"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"অডিঅ’ শ্বেয়াৰ কৰা"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"অডিঅ’ শ্বেয়াৰ কৰি থকা হৈছে"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"বেটাৰী <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"অডিঅ’"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"হেডছেট"</string>
@@ -366,10 +368,8 @@
<string name="thermal" msgid="6758074791325414831">"থাৰ্মেল"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"এখন হাতেৰে ব্যৱহাৰ কৰা ম’ড"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"শুনাৰ ডিভাইচ"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"সক্ৰিয় হৈ আছে"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"সংযোগ বিচ্ছিন্ন কৰা হ’ল"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"শুনাৰ ডিভাইচ"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"নতুন ডিভাইচ পেয়াৰ কৰক"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"নতুন ডিভাইচ পেয়াৰ কৰিবলৈ ক্লিক কৰক"</string>
@@ -1265,8 +1265,8 @@
<string name="rear_display_bottom_sheet_confirm" msgid="1507591562761552899">"এতিয়াই স্ক্ৰীন সলনি কৰক"</string>
<string name="rear_display_folded_bottom_sheet_title" msgid="3930008746560711990">"ফ’নটো আনফ’ল্ড কৰক"</string>
<string name="rear_display_unfolded_bottom_sheet_title" msgid="6291111173057304055">"স্ক্ৰীন সলনি কৰিবনে?"</string>
- <string name="rear_display_folded_bottom_sheet_description" msgid="6842767125783222695">"অধিক ৰিজ’লিউছনৰ বাবে, পিছফালৰ কেমেৰাটো ব্যৱহাৰ কৰক"</string>
- <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"অধিক ৰিজ’লিউছনৰ বাবে, ফ’নটো লুটিয়াই দিয়ক"</string>
+ <string name="rear_display_folded_bottom_sheet_description" msgid="6842767125783222695">"অধিক ৰিজ’লিউশ্বনৰ বাবে, পিছফালৰ কেমেৰাটো ব্যৱহাৰ কৰক"</string>
+ <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"অধিক ৰিজ’লিউশ্বনৰ বাবে, ফ’নটো লুটিয়াই দিয়ক"</string>
<string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"জপাব পৰা ডিভাইচৰ জাপ খুলি থকা হৈছে"</string>
<string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"জপাব পৰা ডিভাইচৰ ওলোটাই থকা হৈছে"</string>
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ফ’ল্ড কৰা"</string>
@@ -1324,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"সন্ধানৰ শ্বৰ্টকাট"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"সংকোচন কৰাৰ চিহ্ন"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"বিস্তাৰ কৰাৰ চিহ্ন"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"অথবা"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"কীব’ৰ্ডৰ বেকলাইট"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dৰ %1$d স্তৰ"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ঘৰৰ সা-সৰঞ্জামৰ নিয়ন্ত্ৰণ"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index b3c0058..592bc06 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Sabah avtomatik aktiv edin"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Cəld Paylaşım və Cihazın Tapılması kimi funksiyalar Bluetooth istifadə edir"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth sabah səhər aktiv ediləcək"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio paylaşma"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audio paylaşılır"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batareya"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Qulaqlıq"</string>
@@ -361,18 +363,13 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cihaz istifadəsinə necə təsir etdi?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Problem növü seçin"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekran qeydəalma"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Performans"</string>
+ <string name="user_interface" msgid="3712869377953950887">"İstifadəçi interfeysi"</string>
+ <string name="thermal" msgid="6758074791325414831">"Termal"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Birəlli rejim"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Eşitmə cihazları"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktiv"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Bağlantı kəsildi"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Eşitmə cihazları"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Yeni cihaz birləşdirin"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Yeni cihaz birləşdirmək üçün klikləyin"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"qatlanmış"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"açıq"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Stilusun enerjisi: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Qələmi adapterə qoşun"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Qələm enerjisi azdır"</string>
<string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Axtarış qısayolları"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"İkonanı yığcamlaşdırın"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"İkonanı genişləndirin"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"və ya"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatura işığı"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Səviyyə %1$d/%2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Ev nizamlayıcıları"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 22afb40..9b27943 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatski ponovo uključi sutra"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funkcije kao što su Quick Share i Pronađi moj uređaj koriste Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth će se uključiti sutra ujutru"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Deljenje zvuka"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Deli se zvuk"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Nivo baterije je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string>
@@ -366,10 +368,8 @@
<string name="thermal" msgid="6758074791325414831">"Termalna kamera"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Režim jednom rukom"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Slušni aparati"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivno"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Veza je prekinuta"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Slušni aparati"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Upari novi uređaj"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite da biste uparili nov uređaj"</string>
@@ -1324,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prečice pretrage"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za skupljanje"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za proširivanje"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvetljenje tastature"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. nivo od %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrole za dom"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 4fe7c5a..ca763cf 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Аўтаматычнае ўключэнне заўтра"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth выкарыстоўваецца такімі функцыямі і сэрвісамі, як Хуткае абагульванне і Знайсці прыладу"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth уключыцца заўтра раніцай"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Абагульванне аўдыя"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Ідзе абагульванне аўдыя"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Узровень зараду: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Гук"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнітура"</string>
@@ -361,18 +363,13 @@
<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>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Прадукцыйнасць"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Карыстальніцкі інтэрфейс"</string>
+ <string name="thermal" msgid="6758074791325414831">"Тэрмальныя паказчыкі"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Рэжым кіравання адной рукой"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слыхавыя апараты"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Актыўныя"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Адключаны"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слыхавыя апараты"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Спалучыць новую прыладу"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Націсніце, каб спалучыць новую прыладу"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"складзена"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"раскладзена"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Зарад акумулятара пяра – <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Падключыце пяро да зараднай прылады"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Нізкі ўзровень зараду пяра"</string>
<string name="video_camera" msgid="7654002575156149298">"Відэакамера"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Пошук спалучэнняў клавіш"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок \"Згарнуць\""</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок \"Разгарнуць\""</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"або"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Падсветка клавіятуры"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Узровень %1$d з %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Кіраванне домам"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 2cd65db..d50b116 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Автоматично включване отново утре"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Функции като „Бързо споделяне“ и „Намиране на устройството ми“ използват Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ще се включи утре сутрин"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Споделяне на звука"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Звукът се споделя"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батерия: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалки"</string>
@@ -361,18 +363,13 @@
<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>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Ефективност"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Потребителски интерфейс"</string>
+ <string name="thermal" msgid="6758074791325414831">"Термално"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим за работа с една ръка"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слухови апарати"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Активно"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Няма връзка"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слухови апарати"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Сдвояване на ново устройство"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Кликнете за сдвояване на ново устройство"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"затворено"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"отворено"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Батерия на писалката: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Свържете писалката към зарядно устройство"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Батерията на писалката е изтощена"</string>
<string name="video_camera" msgid="7654002575156149298">"Видеокамера"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Търсете клавишни комбинации"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за свиване"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за разгъване"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Подсветка на клавиатурата"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Ниво %1$d от %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Контроли за дома"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index f8e350a..bb41b1b 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"আগামীকাল আবার অটোমেটিক চালু হবে"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"দ্রুত শেয়ার ও Find My Device-এর মতো ফিচার ব্লুটুথ ব্যবহার করে"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ব্লুটুথ আগামীকাল সকালে চালু হয়ে যাবে"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"অডিও শেয়ারিং"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"অডিও শেয়ার করা হচ্ছে"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"চার্জ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"অডিও"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"হেডসেট"</string>
@@ -361,18 +363,13 @@
<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>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"পারফর্ম্যান্স"</string>
+ <string name="user_interface" msgid="3712869377953950887">"ইউজার ইন্টারফেস"</string>
+ <string name="thermal" msgid="6758074791325414831">"থার্মাল"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"এক হাতে ব্যবহার করার মোড"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"হিয়ারিং ডিভাইস"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"অ্যাক্টিভ"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ডিসকানেক্ট হয়ে গেছে"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"হিয়ারিং ডিভাইস"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"নতুন ডিভাইস পেয়ার করুন"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"নতুন ডিভাইস পেয়ার করতে ক্লিক করুন"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ফোল্ড করা রয়েছে"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ফোল্ড করা নেই"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"স্টাইলাস ব্যাটারি <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"কোনও চার্জারের সাথে আপনার স্টাইলাস কানেক্ট করুন"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"স্টাইলাস ব্যাটারিতে চার্জ কম আছে"</string>
<string name="video_camera" msgid="7654002575156149298">"ভিডিও ক্যামেরা"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"সার্চ শর্টকাট"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"আইকন আড়াল করুন"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"আইকন বড় করুন"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"অথবা"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"কীবোর্ড ব্যাকলাইট"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d-এর মধ্যে %1$d লেভেল"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"হোম কন্ট্রোল"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 60c936e..4916d80 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatski uključi ponovo sutra"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funkcije kao što su Quick Share i Pronađi moj uređaj koriste Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth će se uključiti sutra ujutro"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Dijeljenje zvuka"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Dijeljenje zvuka"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> baterije"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string>
@@ -361,18 +363,13 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Koji dio uređaja je imao problem?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Odaberite vrstu problema"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snimanje ekrana"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Performanse"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Korisnički interfejs"</string>
+ <string name="thermal" msgid="6758074791325414831">"Termalno"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Način rada jednom rukom"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Slušni aparati"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivno"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Veza je prekinuta"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Slušni aparati"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Uparite novi uređaj"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite da uparite novi uređaj"</string>
@@ -1201,7 +1198,7 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"WiFi se trenutno ne može automatski povezati"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Prikaži sve"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Da promijenite mrežu, isključite ethernet"</string>
- <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Radi poboljšanja iskustva s uređajem aplikacije i usluge i dalje mogu tražiti WiFi mreže bilo kada, čak i kada je WiFi isključen. Ovo možete promijeniti u Postavkama traženja WiFi mreže. "<annotation id="link">"Promijeni"</annotation></string>
+ <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Radi poboljšanja iskustva s uređajem aplikacije i usluge i dalje mogu tražiti WiFi mreže bilo kada, čak i kada je WiFi isključen. Ovo možete promijeniti u postavkama traženja WiFi-ja. "<annotation id="link">"Promijeni"</annotation></string>
<string name="turn_off_airplane_mode" msgid="8425587763226548579">"Isključi način rada u avionu"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> želi dodati sljedeću karticu u Brze postavke"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Dodaj karticu"</string>
@@ -1210,7 +1207,7 @@
<string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# aplikacija je aktivna}one{# aplikacija je aktivna}few{# aplikacije su aktivne}other{# aplikacija je aktivno}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Nove informacije"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktivne aplikacije"</string>
- <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Ove aplikacije su aktivne i pokrenute, čak i kada ih ne koristite. Ovim se poboljšava njihova funkcionalnost, ali može uticati i na vijek trajanja baterije."</string>
+ <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Ove aplikacije su aktivne i pokrenute, čak i kada ih ne koristite. Ovim se poboljšava njihova funkcionalnost, ali to može uticati i na vijek trajanja baterije."</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Zaustavi"</string>
<string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Zaustavljeno"</string>
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Gotovo"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"sklopljeno"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"otklopljeno"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Baterija pisaljke <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Priključite pisaljku na punjač"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Baterija pisaljke je slaba"</string>
<string name="video_camera" msgid="7654002575156149298">"Video kamera"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prečica pretraživanja"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona sužavanja"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona proširivanja"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvjetljenje tastature"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. nivo od %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrole za dom"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index a82d6f1..5afad1b 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Torna\'l a activar automàticament demà"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Les funcions com Quick Share i Troba el meu dispositiu utilitzen el Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"El Bluetooth s\'activarà demà al matí"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Compartició d\'àudio"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"S\'està compartint l\'àudio"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de bateria"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Àudio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculars"</string>
@@ -361,18 +363,13 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"L\'experiència amb el dispositiu s\'ha vist afectada?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecciona el tipus de problema"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravació de pantalla"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Rendiment"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Interfície d\'usuari"</string>
+ <string name="thermal" msgid="6758074791325414831">"Tèrmic"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode d\'una mà"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Audiòfons"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Actiu"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desconnectat"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Audiòfons"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Vincula un dispositiu nou"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Fes clic per vincular un dispositiu nou"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"plegat"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"desplegat"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateria del llapis òptic: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connecta el llapis òptic a un carregador"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Bateria del llapis òptic baixa"</string>
<string name="video_camera" msgid="7654002575156149298">"Càmera de vídeo"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Dreceres de cerca"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Replega la icona"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Desplega la icona"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroil·luminació del teclat"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivell %1$d de %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Controls de la llar"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 9b540ff..b5293c0 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Zítra znovu automaticky zapnout"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth využívají funkce jako Quick Share a Najdi moje zařízení."</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth se zapne zítra ráno."</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Sdílení zvuku"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sdílení zvuku"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterie: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Sluchátka"</string>
@@ -361,18 +363,13 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Co v zařízení bylo ovlivněno?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Vyberte druh problém"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Záznam obrazovky"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Výkon"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Uživatelské rozhraní"</string>
+ <string name="thermal" msgid="6758074791325414831">"Termovize"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Režim jedné ruky"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Naslouchátka"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivní"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Odpojeno"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Naslouchátka"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Spárovat nové zařízení"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknutím spárujete nové zařízení"</string>
@@ -923,7 +920,7 @@
<string name="mobile_data" msgid="4564407557775397216">"Mobilní data"</string>
<string name="mobile_data_text_format" msgid="6806501540022589786">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="mobile_carrier_text_format" msgid="8912204177152950766">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g>, <xliff:g id="MOBILE_DATA_TYPE">%2$s</xliff:g>"</string>
- <string name="wifi_is_off" msgid="5389597396308001471">"Wi-Fi je vypnuta"</string>
+ <string name="wifi_is_off" msgid="5389597396308001471">"Wi-Fi je vypnutá"</string>
<string name="bt_is_off" msgid="7436344904889461591">"Bluetooth je vypnuto"</string>
<string name="dnd_is_off" msgid="3185706903793094463">"Režim Nerušit je vypnut"</string>
<string name="dnd_is_on" msgid="7009368176361546279">"Režim Nerušit je zapnutý"</string>
@@ -933,7 +930,7 @@
<string name="running_foreground_services_title" msgid="5137313173431186685">"Aplikace běžící na pozadí"</string>
<string name="running_foreground_services_msg" msgid="3009459259222695385">"Klepnutím zobrazíte podrobnosti o využití baterie a dat"</string>
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Vypnout mobilní data?"</string>
- <string name="mobile_data_disable_message" msgid="8604966027899770415">"Prostřednictvím <xliff:g id="CARRIER">%s</xliff:g> nebudete moci používat data ani internet. Internet bude dostupný pouze přes Wi-Fi."</string>
+ <string name="mobile_data_disable_message" msgid="8604966027899770415">"Prostřednictvím operátora <xliff:g id="CARRIER">%s</xliff:g> nebudete moct používat data ani internet. Internet bude dostupný pouze přes Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"vašeho operátora"</string>
<string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Přepnout zpět na operátora <xliff:g id="CARRIER">%s</xliff:g>?"</string>
<string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Mobilní data se nebudou automaticky přepínat podle dostupnosti"</string>
@@ -1211,7 +1208,7 @@
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Nové informace"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktivní aplikace"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Tyto aplikace jsou spuštěné a aktivní, i když je nepoužíváte. Zlepšuje to jejich funkčnost, ale může to mít dopad na výdrž baterie."</string>
- <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Konec"</string>
+ <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Zastavit"</string>
<string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Zastaveno"</string>
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Hotovo"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Zkopírováno"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"složené"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"rozložené"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Baterie dotykového pera <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Připojte dotykové pero k nabíječce"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Slabá baterie dotykového pera"</string>
<string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Vyhledat zkratky"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona sbalení"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozbalení"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"nebo"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podsvícení klávesnice"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Úroveň %1$d z %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Ovládání domácnosti"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 57353f7..6e2323b 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktivér automatisk igen i morgen"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funktioner som f.eks. Quick Share og Find min enhed anvender Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth aktiveres i morgen tidlig"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Lyddeling"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Deler lyd"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Lyd"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -361,18 +363,13 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Hvilken del af din enhedsoplevelse blev påvirket?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Vælg problemtype"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skærmoptagelse"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Ydeevne"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Brugerflade"</string>
+ <string name="thermal" msgid="6758074791325414831">"Termisk"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enhåndstilstand"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Høreapparater"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivt"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Afbrudt"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Høreapparater"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Par ny enhed"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik for at parre en ny enhed"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"foldet"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"foldet ud"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batteriniveau på styluspen: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Slut din styluspen til en oplader"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Lavt batteriniveau på styluspen"</string>
<string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Genveje til søgning"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikon for Skjul"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikon for Udvid"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastaturets baggrundslys"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d af %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Hjemmestyring"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index e7fa9b1..bf28a88 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Morgen automatisch wieder aktivieren"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funktionen wie „Quick Share“ und „Mein Gerät finden“ verwenden Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth wird morgen früh aktiviert"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audiofreigabe"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audioinhalte werden freigegeben"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akkustand: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -361,18 +363,13 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Welche Bereiche des Geräts waren betroffen?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Art des Problems auswählen"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Bildschirmaufnahme"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Leistung"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Benutzeroberfläche"</string>
+ <string name="thermal" msgid="6758074791325414831">"Überhitzung"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Einhandmodus"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hörgeräte"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktiv"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Nicht verbunden"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hörgeräte"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Neues Gerät koppeln"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klicken, um neues Gerät zu koppeln"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"zugeklappt"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"aufgeklappt"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Akkustand des Eingabestifts: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Schließe deinen Eingabestift an ein Ladegerät an"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Stylus-Akkustand niedrig"</string>
<string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tastenkürzel suchen"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Symbol „Minimieren“"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Symbol „Maximieren“"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"oder"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastaturbeleuchtung"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d von %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Smart-Home-Steuerung"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 23d704d..3dd5fc0 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Αυτόματη ενεργοποίηση ξανά αύριο"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Λειτουργίες όπως το Quick Share και η Εύρεση συσκευής χρησιμοποιούν το Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Το Bluetooth θα ενεργοποιηθεί αύριο το πρωί"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Κοινή χρήση ήχου"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Κοινή χρήση ήχου"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Μπαταρία <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ήχος"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ακουστικά"</string>
@@ -361,18 +363,13 @@
<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>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Απόδοση"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Διεπαφή χρήστη"</string>
+ <string name="thermal" msgid="6758074791325414831">"Θερμικό"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Λειτουργία ενός χεριού"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Συσκευές ακοής"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ενεργά"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Αποσυνδεδεμένα"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Συσκευές ακοής"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Σύζευξη νέας συσκευής"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Κάντε κλικ για σύζευξη νέας συσκευής"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"διπλωμένη"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ξεδιπλωμένη"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Μπαταρία γραφίδας <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Συνδέστε τη γραφίδα σε έναν φορτιστή"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Χαμηλή στάθμη μπαταρίας γραφίδας"</string>
<string name="video_camera" msgid="7654002575156149298">"Βιντεοκάμερα"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Συντομεύσεις αναζήτησης"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Εικονίδιο σύμπτυξης"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Εικονίδιο ανάπτυξης"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ή"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Οπίσθιος φωτισμός πληκτρολογίου"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Επίπεδο %1$d από %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Οικιακοί έλεγχοι"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index d71ff1c..e830f3c 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Features like Quick Share and Find My Device use Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio sharing"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sharing audio"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -366,10 +368,8 @@
<string name="thermal" msgid="6758074791325414831">"Thermal"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hearing devices"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Active"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Disconnected"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hearing devices"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Pair new device"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string>
@@ -1324,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Home controls"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 44e9d25..046ce28 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -281,8 +281,8 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Features like Quick Share and Find My Device use Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio Sharing"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sharing Audio"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Share audio"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Sharing audio"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -1322,8 +1322,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Home Controls"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index d71ff1c..e830f3c 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Features like Quick Share and Find My Device use Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio sharing"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sharing audio"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -366,10 +368,8 @@
<string name="thermal" msgid="6758074791325414831">"Thermal"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hearing devices"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Active"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Disconnected"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hearing devices"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Pair new device"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string>
@@ -1324,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Home controls"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index d71ff1c..e830f3c 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Features like Quick Share and Find My Device use Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio sharing"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sharing audio"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -366,10 +368,8 @@
<string name="thermal" msgid="6758074791325414831">"Thermal"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hearing devices"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Active"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Disconnected"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hearing devices"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Pair new device"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string>
@@ -1324,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Home controls"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index c2f7098..07907a6 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -281,8 +281,8 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Features like Quick Share and Find My Device use Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio Sharing"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sharing Audio"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Share audio"</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Sharing audio"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -1322,8 +1322,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Home Controls"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index fd800ad..c622ebe 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Activar automáticamente mañana"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Las funciones como Quick Share y Encontrar mi dispositivo usan Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"El Bluetooth se activará mañana a la mañana"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Uso compartido de audio"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Compartiendo audio"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string>
@@ -361,18 +363,13 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"¿Qué parte de tu exp. del disp. se vio afectada?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Seleccionar tipo de problema"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Grabadora de pant."</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Rendimiento"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Interfaz de usuario"</string>
+ <string name="thermal" msgid="6758074791325414831">"Térmico"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo una mano"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Dispositivos auditivos"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Activos"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desconectados"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Dispositivos auditivos"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Vincular dispositivo nuevo"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Haz clic para vincular un dispositivo nuevo"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"plegado"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"desplegado"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batería de la pluma stylus: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecta tu pluma stylus a un cargador"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"La pluma stylus tiene poca batería"</string>
<string name="video_camera" msgid="7654002575156149298">"Videocámara"</string>
@@ -1328,10 +1324,9 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Buscar combinaciones de teclas"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícono de contraer"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícono de expandir"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación del teclado"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Controles de la casa"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"Accede rápidamente a controles de la casa como prot. de pantalla"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"Usa rápidamente los controles de la casa como protector de pantalla"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 5bcf582..c021b45 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Volver a activar automáticamente mañana"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Las funciones como Quick Share y Encontrar mi dispositivo usan Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"El Bluetooth se activará mañana por la mañana"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Compartir audio"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Compartiendo audio"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string>
@@ -361,18 +363,13 @@
<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>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Rendimiento"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Interfaz de usuario"</string>
+ <string name="thermal" msgid="6758074791325414831">"Térmico"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo Una mano"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Audífonos"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Activos"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desconectados"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Audífonos"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Emparejar nuevo dispositivo"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Haz clic para emparejar un nuevo dispositivo"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"plegado"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"desplegado"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batería del lápiz óptico al <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecta tu lápiz óptico a un cargador"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Batería del lápiz óptico baja"</string>
<string name="video_camera" msgid="7654002575156149298">"Videocámara"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atajos de búsqueda"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icono de contraer"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icono de desplegar"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación del teclado"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Controles de la casa"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index e7d5e2f..003c925 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Lülita automaatselt homme uuesti sisse"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Sellised funktsioonid nagu Kiirjagamine ja Leia mu seade kasutavad Bluetoothi."</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth lülitub sisse homme hommikul"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Heli jagamine"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Heli jagamine"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> akut"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Heli"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Peakomplekt"</string>
@@ -361,18 +363,13 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Millist seadme kasutuskogemuse osa see mõjutas?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Valige probleemi tüüp"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekraanisalvestus"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Jõudlus"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Kasutajaliides"</string>
+ <string name="thermal" msgid="6758074791325414831">"Soojus"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Ühekäerežiim"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Kuuldeseadmed"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ühendatud"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Ühendus on katkestatud"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Kuuldeseadmed"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Uue seadme sidumine"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Uue seadme sidumiseks klõpsake"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"kokku volditud"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"lahti volditud"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Elektronpliiatsi aku <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Ühendage elektronpliiats laadijaga"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Elektronpliiatsi akutase on madal"</string>
<string name="video_camera" msgid="7654002575156149298">"Videokaamera"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Otsingu otseteed"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ahendamisikoon"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Laiendamisikoon"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"või"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatuuri taustavalgustus"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Tase %1$d/%2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kodu juhtelemendid"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 5e4c5ac..66b79b8 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktibatu automatikoki berriro bihar"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share, Bilatu nire gailua eta beste eginbide batzuek Bluetootha erabiltzen dute"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bihar goizean aktibatuko da Bluetootha"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audioa partekatzea"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audioa partekatzen"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audioa"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Entzungailua"</string>
@@ -361,18 +363,13 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Gailuaren erabileraren zer alderdiri eragin dio?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Hautatu arazo mota"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Pantaila-grabaketa"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Errendimendua"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Erabiltzaile-interfazea"</string>
+ <string name="thermal" msgid="6758074791325414831">"Termikoa"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Esku bakarreko modua"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Entzumen-gailuak"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktibo"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Deskonektatuta"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Entzumen-gailuak"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Parekatu beste gailu bat"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Egin klik beste gailu bat parekatzeko"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"tolestuta"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"tolestu gabe"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Arkatzaren bateria: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Konektatu arkatza kargagailu batera"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Arkatzak bateria gutxi du"</string>
<string name="video_camera" msgid="7654002575156149298">"Bideokamera"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Bilatu lasterbideak"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Tolesteko ikonoa"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Zabaltzeko ikonoa"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"edo"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Teklatuaren hondoko argia"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d/%2$d maila"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Etxeko gailuen kontrola"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index ad24c1b..cdbb685 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"فردا دوباره بهطور خودکار روشن شود"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ویژگیهایی مثل «همرسانی سریع» و «پیدا کردن دستگاهم» از بلوتوث استفاده میکنند"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"بلوتوث فردا صبح روشن خواهد شد"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"اشتراک صدا"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"درحال اشتراکگذاری صدا"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"شارژ باتری <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"صوت"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"هدست"</string>
@@ -361,24 +363,19 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"کدام بخش تجربه استفاده از دستگاه تحتتأثیر قرار گرفت؟"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"انتخاب نوع مشکل"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ضبط صفحهنمایش"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"عملکرد"</string>
+ <string name="user_interface" msgid="3712869377953950887">"میانای کاربر"</string>
+ <string name="thermal" msgid="6758074791325414831">"حرارتی"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"حالت یکدستی"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"سمعک"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"فعال"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"اتصال قطع شد"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"سمعک"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"جفت کردن دستگاه جدید"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"برای جفت کردن دستگاه جدید، کلیک کنید"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"پیشتنظیم بهروزرسانی نشد"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"پیشتنظیم"</string>
- <string name="live_caption_title" msgid="8916875614623730005">"زیرنویس زنده"</string>
+ <string name="live_caption_title" msgid="8916875614623730005">"زیرنویس ناشنوایان زنده"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"میکروفون دستگاه لغو انسداد شود؟"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"دوربین دستگاه لغو انسداد شود؟"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"دوربین و میکروفون دستگاه لغو انسداد شود؟"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"تاشده"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"تانشده"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"شارژ باتری قلم <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"قلم را به شارژر وصل کنید"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"باتری قلم ضعیف است"</string>
<string name="video_camera" msgid="7654002575156149298">"دوربین ویدیویی"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"جستجوی میانبرها"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"نماد جمع کردن"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"نماد ازهم بازکردن"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"یا"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"نور پسزمینه صفحهکلید"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"سطح %1$d از %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"کنترل خانه هوشمند"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 0fed3d5..bea39cb 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Laita automaattisesti päälle taas huomenna"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share, Paikanna laite ja tietyt muut ominaisuudet käyttävät Bluetoothia"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth menee päälle huomisaamuna"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audionjako"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audiota jaetaan"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akun taso <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ääni"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -361,18 +363,13 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Mitä osaa käyttökokemuksesta ongelma koski?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Valitse ongelman tyyppi"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Näytön tallentaja"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Suorituskyky"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Käyttöliittymä"</string>
+ <string name="thermal" msgid="6758074791325414831">"Lämpökamera"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Yhden käden moodi"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Kuulolaitteet"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktiivinen"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Yhteys katkaistu"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Kuulolaitteet"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Muodosta uusi laitepari"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Muodosta uusi laitepari klikkaamalla"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"taitettu"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"taittamaton"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Näyttökynän akku <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Yhdistä näyttökynä laturiin"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Näyttökynän akku vähissä"</string>
<string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pikahaut"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Tiivistyskuvake"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Laajennuskuvake"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"tai"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Näppämistön taustavalo"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Taso %1$d/%2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kodin ohjaus"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 95c788a..95b25936 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Activer le Bluetooth automatiquement demain"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Les fonctionnalités comme Partage rapide et Localiser mon appareil utilisent le Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Le Bluetooth s\'activera demain matin"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Partage audio"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Partage de l\'audio en cours…"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Pile : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Écouteurs"</string>
@@ -361,18 +363,13 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Quelle composante de l\'appareil a été affectée?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Sélectionner un type"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Enregistrement écran"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Performance"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Interface utilisateur"</string>
+ <string name="thermal" msgid="6758074791325414831">"Thermique"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode Une main"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Appareils auditifs"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Actives"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Déconnectées"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Appareils auditifs"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Associer un nouvel appareil"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Cliquez ici pour associer un nouvel appareil"</string>
@@ -1209,8 +1206,8 @@
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Choisir l\'utilisateur"</string>
<string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# application est active}one{# application est active}many{# d\'applications sont actives}other{# applications sont actives}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Nouvelle information"</string>
- <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Applications actives"</string>
- <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Ces applications sont actives et s\'exécutent même lorsque vous ne les utilisez pas. Cela améliore leur fonctionnalité, mais peut également affecter l\'autonomie de la pile."</string>
+ <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Applis actives"</string>
+ <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Ces applis sont actives et s\'exécutent même lorsque vous ne les utilisez pas. Cela améliore leur fonctionnalité, mais peut également affecter l\'autonomie de la pile."</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Arrêter"</string>
<string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Arrêtée"</string>
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"OK"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"plié"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"déplié"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Pile du stylet <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connectez votre stylet à un chargeur"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Pile du stylet faible"</string>
<string name="video_camera" msgid="7654002575156149298">"Caméra"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Recherchez des raccourcis"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icône Réduire"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icône Développer"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Rétroéclairage du clavier"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d de %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Domotique"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index b7abd0e..1b8ddc1 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Réactiver automatiquement demain"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Certaines fonctionnalités, telles que Quick Share et Localiser mon appareil, utilisent le Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Le Bluetooth sera activé demain matin"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Partage audio"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audio partagé"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batterie"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Casque"</string>
@@ -361,18 +363,13 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Quel problème avez-vous rencontré avec votre appareil ?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Sélectionnez un type de problème"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Enregistrement de l\'écran"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Performances"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Interface utilisateur"</string>
+ <string name="thermal" msgid="6758074791325414831">"Thermique"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode une main"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Appareils auditifs"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Actifs"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Déconnectés"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Appareils auditifs"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Associer un nouvel appareil"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Cliquer pour associer un nouvel appareil"</string>
@@ -1212,7 +1209,7 @@
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Applis actives"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Ces applis sont actives et s\'exécutent même lorsque vous ne les utilisez pas. Cela améliore leur fonctionnement, mais peut également affecter l\'autonomie de la batterie."</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Arrêter"</string>
- <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Arrêtée"</string>
+ <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Arrêté"</string>
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"OK"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Copié"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"De <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"plié"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"déplié"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batterie du stylet à <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connectez votre stylet à un chargeur"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"La batterie du stylet est faible"</string>
<string name="video_camera" msgid="7654002575156149298">"Caméra"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Raccourcis de recherche"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icône Réduire"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icône Développer"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Rétroéclairage du clavier"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d sur %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Contrôle de la maison"</string>
diff --git a/packages/SystemUI/res/values-fr/tiles_states_strings.xml b/packages/SystemUI/res/values-fr/tiles_states_strings.xml
index 23c124c..fcdd9f0 100644
--- a/packages/SystemUI/res/values-fr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fr/tiles_states_strings.xml
@@ -58,8 +58,8 @@
</string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Indisponible"</item>
- <item msgid="5044688398303285224">"Désactivée"</item>
- <item msgid="8527389108867454098">"Activée"</item>
+ <item msgid="5044688398303285224">"Désactivé"</item>
+ <item msgid="8527389108867454098">"Activé"</item>
</string-array>
<string-array name="tile_states_rotation">
<item msgid="4578491772376121579">"Indisponible"</item>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 55dd05a..1d0f4c4 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Volver activar automaticamente mañá"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"As funcións como Quick Share e Localizar o meu dispositivo utilizan o Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"O Bluetooth activarase mañá á mañá"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio compartido"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Compartindo audio"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string>
@@ -361,18 +363,13 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cal foi o problema na experiencia co dispositivo?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecciona o tipo de problema"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravación de pant."</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Rendemento"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Interface de usuario"</string>
+ <string name="thermal" msgid="6758074791325414831">"Térmico"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo dunha soa man"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Dispositivos auditivos"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Activos"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desconectados"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Dispositivos auditivos"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Vincular dispositivo novo"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Fai clic para vincular un novo dispositivo"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"dispositivo pregado"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"dispositivo despregado"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batería do lapis óptico: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecta o lapis óptico a un cargador"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"O lapis óptico ten pouca batería"</string>
<string name="video_camera" msgid="7654002575156149298">"Videocámara"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atallos de busca"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icona de contraer"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icona de despregar"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación do teclado"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Controis domóticos"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 425a768..7a4cfd1 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"આવતીકાલે ફરીથી ઑટોમૅટિક રીતે ચાલુ કરો"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ક્વિક શેર અને Find My Device જેવી સુવિધાઓ બ્લૂટૂથનો ઉપયોગ કરે છે"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"બ્લૂટૂથ આવતીકાલે સવારે ચાલુ થશે"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ઑડિયો શેરિંગ"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ઑડિયો શેર કરી રહ્યાં છીએ"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> બૅટરી"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ઑડિયો"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"હૅડસેટ"</string>
@@ -361,18 +363,13 @@
<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>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"પર્ફોર્મન્સ"</string>
+ <string name="user_interface" msgid="3712869377953950887">"યૂઝર ઇન્ટરફેસ"</string>
+ <string name="thermal" msgid="6758074791325414831">"થર્મલ"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"એક-હાથે વાપરો મોડ"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"સાંભળવામાં મદદ આપતા ડિવાઇસ"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"સક્રિય"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ડિસ્કનેક્ટેડ છે"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"સાંભળવામાં મદદ આપતા ડિવાઇસ"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"નવા ડિવાઇસ સાથે જોડાણ કરો"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"નવા ડિવાઇસ સાથે જોડાણ કરવા માટે ક્લિક કરો"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ફોલ્ડ કરેલું"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"અનફોલ્ડ કરેલું"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"સ્ટાઇલસની બૅટરી <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"તમારા સ્ટાઇલસને ચાર્જર સાથે કનેક્ટ કરો"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"સ્ટાઇલસની બૅટરીમાં ચાર્જ ઓછો છે"</string>
<string name="video_camera" msgid="7654002575156149298">"વીડિયો કૅમેરા"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"શૉર્ટકટ શોધો"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"\'નાનું કરો\'નું આઇકન"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"\'મોટું કરો\'નું આઇકન"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"અથવા"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"કીબોર્ડની બૅકલાઇટ"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dમાંથી %1$d લેવલ"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ઘરેલું સાધનોના નિયંત્રણો"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 8f7b87c..896af24 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"कल फिर से अपने-आप चालू हो जाए"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"क्विक शेयर और Find My Device जैसी सुविधाएं, ब्लूटूथ का इस्तेमाल करती हैं"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ब्लूटूथ कल सुबह चालू होगा"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ऑडियो शेयर करने की सुविधा"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ऑडियो शेयर किया जा रहा है"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> बैटरी"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ऑडियो"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string>
@@ -361,18 +363,13 @@
<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>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"परफ़ॉर्मेंस"</string>
+ <string name="user_interface" msgid="3712869377953950887">"यूज़र इंटरफ़ेस"</string>
+ <string name="thermal" msgid="6758074791325414831">"थर्मल"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"वन-हैंडेड मोड"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"कान की मशीनें"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ऐक्टिव"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"डिसकनेक्ट हो गया"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"कान की मशीनें"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"नया डिवाइस जोड़ें"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"नया डिवाइस जोड़ने के लिए क्लिक करें"</string>
@@ -1112,7 +1109,7 @@
<string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> डिवाइस चुने गए"</string>
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(डिसकनेक्ट हो गया)"</string>
<string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"स्विच नहीं किया जा सकता. फिर से कोशिश करने के लिए टैप करें."</string>
- <string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"किसी डिवाइस को कनेक्ट करें"</string>
+ <string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"कोई डिवाइस कनेक्ट करें"</string>
<string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"इस सेशन को कास्ट करने के लिए, कृपया ऐप्लिकेशन खोलें."</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"अनजान ऐप्लिकेशन"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"कास्ट करना बंद करें"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"डिवाइस फ़ोल्ड किया गया"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"डिवाइस अनफ़ोल्ड किया गया"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"स्टाइलस की बैटरी <xliff:g id="PERCENTAGE">%s</xliff:g> है"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"अपने स्टाइलस को चार्ज करें"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"स्टाइलस की बैटरी कम है"</string>
<string name="video_camera" msgid="7654002575156149298">"वीडियो कैमरा"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"सर्च शॉर्टकट"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"छोटा करने का आइकॉन"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"बड़ा करने का आइकॉन"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"या"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"कीबोर्ड की बैकलाइट"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d में से %1$d लेवल"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"होम कंट्रोल"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index bc84c1d..acf253d 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatski uključi ponovno sutra"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Značajke kao što su brzo dijeljenje i Pronađi moj uređaj koriste Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth će se uključiti sutra ujutro"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Zajedničko slušanje"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Zajedničko slušanje"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> baterije"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string>
@@ -361,18 +363,13 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Na koji dio doživljaja na uređaju to utjecalo?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Odaberite vrstu problema"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snimanje zaslona"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Izvedba"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Korisničko sučelje"</string>
+ <string name="thermal" msgid="6758074791325414831">"Termalno"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Način rada jednom rukom"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Slušna pomagala"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivno"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Nije povezano"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Slušna pomagala"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Uparite novi uređaj"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite da biste uparili novi uređaj"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"zatvoreno"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"otvoreno"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Baterija olovke <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Priključite pisaljku na punjač"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Slaba baterija pisaljke"</string>
<string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prečaci za pretraživanje"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za sažimanje"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za proširivanje"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvjetljenje tipkovnice"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Razina %1$d od %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Upravljanje uređajima"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 4b36026..c73511e 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatikus visszakapcsolás holnap"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Egyes funkciók (például a Quick Share és a Készülékkereső) Bluetootht használnak"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"A Bluetooth holnap reggel bekapcsol"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Hang megosztása"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Hang megosztása…"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akkumulátor: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Hang"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -292,7 +294,7 @@
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatikus elforgatás"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatikus képernyőforgatás"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Tartózkodási hely"</string>
- <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Képernyővédő"</string>
+ <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Képernyőkímélő"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Hozzáférés a kamerához"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofonelérés"</string>
<string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Rendelkezésre áll"</string>
@@ -361,18 +363,13 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Az eszközhasználati élmény mely része érintett?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Problématípus kiválasztása"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Képernyőrögzítés"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Teljesítmény"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Kezelőfelület"</string>
+ <string name="thermal" msgid="6758074791325414831">"Termikus"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Egykezes mód"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hallókészülékek"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktív"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Leválasztva"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hallókészülékek"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Új eszköz párosítása"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kattintson új eszköz párosításához"</string>
@@ -601,7 +598,7 @@
<string name="screen_pinning_exit" msgid="4553787518387346893">"Alkalmazás kitűzése megszüntetve"</string>
<string name="stream_voice_call" msgid="7468348170702375660">"Telefonhívás"</string>
<string name="stream_system" msgid="7663148785370565134">"Rendszer"</string>
- <string name="stream_ring" msgid="7550670036738697526">"Csörgetés"</string>
+ <string name="stream_ring" msgid="7550670036738697526">"Csörgés"</string>
<string name="stream_music" msgid="2188224742361847580">"Média"</string>
<string name="stream_alarm" msgid="16058075093011694">"Ébresztő"</string>
<string name="stream_notification" msgid="7930294049046243939">"Értesítés"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"összehajtva"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"kihajtva"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Érintőceruza töltöttségi szintje: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Tegye töltőre az érintőceruzát"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Az érintőceruza töltöttsége alacsony"</string>
<string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Billentyűparancsok keresése"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Összecsukás ikon"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Kibontás ikon"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"vagy"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"A billentyűzet háttérvilágítása"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Fényerő: %2$d/%1$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Otthon vezérlése"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index d8e36ad..f1462be 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Վաղը նորից ավտոմատ միացնել"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth-ն օգտագործում են, օրինակ, Quick Share և «Գտնել իմ սարքը» գործառույթները"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth-ը կմիանա վաղն առավոտյան"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Աուդիոյի փոխանցում"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Աուդիոն փոխանցվում է"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Մարտկոցի լիցքը՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Աուդիո"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ականջակալ"</string>
@@ -361,18 +363,13 @@
<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>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Արդյունավետություն"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Օգտատիրական ինտերֆեյս"</string>
+ <string name="thermal" msgid="6758074791325414831">"Ջերմատեսիլ"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Մեկ ձեռքի ռեժիմ"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Լսողական սարքեր"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ակտիվ է"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Անջատված է"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Լսողական սարքեր"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Նոր սարքի զուգակցում"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Սեղմեք՝ նոր սարք զուգակցելու համար"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ծալված"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"բացված"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Ստիլուսի մարտկոցի լիցքը՝ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Ձեր ստիլուսը միացրեք լիցքավորիչի"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Ստիլուսի մարտկոցի լիցքի ցածր մակարդակ"</string>
<string name="video_camera" msgid="7654002575156149298">"Տեսախցիկ"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Դյուրանցումների որոնում"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ծալել պատկերակը"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ծավալել պատկերակը"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"կամ"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Հետին լուսավորությամբ ստեղնաշար"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d՝ %2$d-ից"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Տան կառավարման տարրեր"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 22d0de5..7525e9a 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Otomatis aktifkan lagi besok"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Fitur seperti Quick Share dan Temukan Perangkat Saya menggunakan Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth akan dinyalakan besok pagi"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Berbagi Audio"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Berbagi Audio"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterai <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -361,18 +363,13 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Bagian pengalaman perangkat mana yang terpengaruh?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Pilih jenis masalah"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Perekaman layar"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Performa"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Antarmuka Pengguna"</string>
+ <string name="thermal" msgid="6758074791325414831">"Termal"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode satu tangan"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Alat bantu dengar"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktif"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Terputus"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Alat bantu dengar"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Sambungkan perangkat baru"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik untuk menyambungkan perangkat baru"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ditutup"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"dibuka"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Baterai stilus <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Hubungkan stilus ke pengisi daya"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Baterai stilus lemah"</string>
<string name="video_camera" msgid="7654002575156149298">"Kamera video"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pintasan penelusuran"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikon ciutkan"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikon luaskan"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"atau"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Lampu latar keyboard"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Tingkat %1$d dari %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrol Rumah"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index b631c47..3850790 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Kveikja sjálfkrafa aftur á morgun"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Eiginleikar eins og Flýtideiling og Finna tækið mitt nota Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Kveikt verður á Bluetooth í fyrramálið"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Hljóði deilt"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Deilir hljóði"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> rafhlöðuhleðsla"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Hljóð"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Höfuðtól"</string>
@@ -361,18 +363,13 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Hvað í tækjaupplifuninni varð fyrir áhrifum?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Veldu gerð vandamáls"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skjáupptaka"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Afköst"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Notandaviðmót"</string>
+ <string name="thermal" msgid="6758074791325414831">"Varmi"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Einhent stilling"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Heyrnartæki"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Virk"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Aftengd"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Heyrnartæki"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Para nýtt tæki"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Smelltu til að para nýtt tæki"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"samanbrotið"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"opið"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Pennarafhlaða <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Tengdu pennann við hleðslutæki"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Rafhlaða pennans er að tæmast"</string>
<string name="video_camera" msgid="7654002575156149298">"Kvikmyndatökuvél"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Leitarflýtileiðir"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Minnka tákn"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Stækka tákn"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eða"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Baklýsing lyklaborðs"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Stig %1$d af %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Heimastýringar"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 5494b16..a0d54c267 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Riattiva automaticamente domani"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funzionalità come Quick Share e Trova il mio dispositivo usano il Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Il Bluetooth verrà attivato domani mattina"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Condivisione audio"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Condivisione audio in corso…"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batteria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auricolare"</string>
@@ -366,10 +368,8 @@
<string name="thermal" msgid="6758074791325414831">"Termico"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modalità a una mano"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Apparecchi acustici"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Attivi"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Disconnessi"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Protesi uditive"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Accoppia nuovo dispositivo"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Fai clic per accoppiare un nuovo dispositivo"</string>
@@ -1324,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Scorciatoie per la ricerca"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icona Comprimi"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icona Espandi"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"oppure"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroilluminazione della tastiera"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Livello %1$d di %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Controlli della casa"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 2ad95fb..f773b84 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -278,11 +278,13 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"נשמר"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ניתוק"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"הפעלה"</string>
- <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"החיבור יופעל שוב אוטומטית מחר"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"יופעל שוב אוטומטית מחר"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"תכונות כמו \'שיתוף מהיר\' ו\'איפה המכשיר שלי\' משתמשות ב-Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"חיבור ה-Bluetooth יופעל מחר בבוקר"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"שיתוף אודיו"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"מתבצע שיתוף של האודיו"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> סוללה"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"אודיו"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"אוזניות"</string>
@@ -366,10 +368,8 @@
<string name="thermal" msgid="6758074791325414831">"תרמי"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"מצב שימוש ביד אחת"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"מכשירי שמיעה"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"פעיל"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"מנותק"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"מכשירי שמיעה"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"התאמה של מכשיר חדש"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"צריך ללחוץ כדי להתאים מכשיר חדש"</string>
@@ -620,7 +620,7 @@
<string name="volume_panel_noise_control_title" msgid="7413949943872304474">"בקרת רעש"</string>
<string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"אודיו מרחבי"</string>
<string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"השבתה"</string>
- <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"מצב קבוע"</string>
+ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"מצב סטטי"</string>
<string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"מעקב אחר תנועות הראש"</string>
<string name="volume_ringer_change" msgid="3574969197796055532">"יש להקיש כדי לשנות את מצב תוכנת הצלצול"</string>
<string name="volume_ringer_hint_mute" msgid="4263821214125126614">"השתקה"</string>
@@ -1198,7 +1198,7 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"ה-Wi-Fi לא יתחבר באופן אוטומטי בינתיים"</string>
<string name="see_all_networks" msgid="3773666844913168122">"הצגת הכול"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"כדי לעבור בין רשתות, צריך לנתק את האתרנט"</string>
- <string name="wifi_scan_notify_message" msgid="3753839537448621794">"כדי לשפר את חוויית השימוש במכשיר, אפליקציות ושירותים יוכלו לחפש רשתות Wi-Fi בכל שלב, גם כאשר ה-Wi-Fi כבוי. אפשר לשנות זאת בהגדרות של חיפוש נקודות Wi-Fi. "<annotation id="link">"שינוי"</annotation></string>
+ <string name="wifi_scan_notify_message" msgid="3753839537448621794">"כדי לשפר את חוויית השימוש במכשיר, אפליקציות ושירותים יוכלו לחפש רשתות Wi-Fi בכל שלב, גם כאשר ה-Wi-Fi כבוי. אפשר לשנות את זה בהגדרות של חיפוש נקודות Wi-Fi. "<annotation id="link">"שינוי"</annotation></string>
<string name="turn_off_airplane_mode" msgid="8425587763226548579">"השבתה של מצב טיסה"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"אפליקציית <xliff:g id="APPNAME">%1$s</xliff:g> מבקשת להוסיף להגדרות המהירות את הלחצן הבא"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"להוסיף לחצן"</string>
@@ -1324,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"קיצורי דרך לחיפוש"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"סמל הכיווץ"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"סמל ההרחבה"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"או"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"התאורה האחורית במקלדת"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"רמה %1$d מתוך %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"שליטה במכשירים"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 15c1f8b..6b5d082 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明日自動的に ON に戻す"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share や「デバイスを探す」などの機能は Bluetooth を使用します"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"明日の朝に Bluetooth が ON になります"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"音声の共有"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"音声を共有中"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"バッテリー <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"オーディオ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ヘッドセット"</string>
@@ -366,10 +368,8 @@
<string name="thermal" msgid="6758074791325414831">"温度"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"片手モード"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"補聴器"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"アクティブ"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"未接続"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"補聴器"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"新しいデバイスとペア設定"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"クリックすると、新しいデバイスをペア設定できます"</string>
@@ -1324,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"検索ショートカット"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"閉じるアイコン"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"開くアイコン"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"または"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"キーボード バックライト"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"レベル %1$d/%2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ホーム コントロール"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 4d17179..399c1f0 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ხელახლა ავტომატურად ჩართვა ხვალ"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ისეთი ფუნქციები, როგორიცაა სწრაფი გაზიარება და ჩემი მოწყობილობის პოვნა, იყენებს Bluetooth-ს"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ჩაირთვება ხვალ დილით"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"აუდიოს გაზიარება"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"აუდიოს გაზიარება"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ბატარეა"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"აუდიო"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ყურსაცვამი"</string>
@@ -366,16 +368,14 @@
<string name="thermal" msgid="6758074791325414831">"თერმული"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ცალი ხელის რეჟიმი"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"სმენის აპარატები"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"აქტიური"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"კავშირი გაწყვეტილია"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"სმენის აპარატები"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ახალი მოწყობილობის დაწყვილება"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"დააწკაპუნეთ ახალი მოწყობილობის დასაწყვილებლად"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"წინასწარ დაყენებული პარამეტრების განახლება ვერ მოხერხდა"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"წინასწარ დაყენებული"</string>
- <string name="live_caption_title" msgid="8916875614623730005">"პირდაპირი სუბტიტრები"</string>
+ <string name="live_caption_title" msgid="8916875614623730005">"ავტოსუბტიტრები"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"გსურთ მოწყობილობის მიკროფონის განბლოკვა?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"გსურთ მოწყობილობის კამერის განბლოკვა?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"გსურთ მოწყობილობის კამერის და მიკროფონის განბლოკვა?"</string>
@@ -1324,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ძიების მალსახმობები"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ხატულის ჩაკეცვა"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ხატულის გაფართოება"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ან"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"კლავიატურის შენათება"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"დონე: %1$d %2$d-დან"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"სახლის კონტროლი"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 08e9fa2..a3e6179 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ертең автоматты түрде қосылсын"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share және Find My Device сияқты функциялар Bluetooth-ты пайдаланады."</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ертең таңертең қосылады."</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Аудио бөлісу"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Аудио бөлісу әрекеті орындалып жатыр."</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батарея деңгейі: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Aудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string>
@@ -361,18 +363,13 @@
<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>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Өнімділік режимі"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Пайдаланушы интерфейсі"</string>
+ <string name="thermal" msgid="6758074791325414831">"Термовизия"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Бір қолмен басқару режимі"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Есту құрылғылары"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Қосулы"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Ажыратулы"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Есту құрылғылары"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Жаңа құрылғыны жұптау"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Жаңа құрылғыны жұптау үшін басыңыз."</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"жабық"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ашық"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Стилус батареясы: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Стилусты зарядтағышқа жалғаңыз."</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Стилус батареясының заряды аз"</string>
<string name="video_camera" msgid="7654002575156149298">"Бейнекамера"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Іздеу жылдам пәрмендері"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Жию белгішесі"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Жаю белгішесі"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"немесе"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Пернетақта жарығы"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Деңгей: %1$d/%2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Үй басқару элементтері"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index d6538bd..7c6f127 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"បើកដោយស្វ័យប្រវត្តិម្ដងទៀតនៅថ្ងៃស្អែក"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"មុខងារដូចជា Quick Share និង \"រកឧបករណ៍របស់ខ្ញុំ\" ប្រើប៊្លូធូស"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ប៊្លូធូសនឹងបើកនៅព្រឹកស្អែក"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ការស្ដាប់សំឡេងរួមគ្នា"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"កំពុងស្ដាប់សំឡេងរួមគ្នា"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"ថ្ម <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"សំឡេង"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"កាស"</string>
@@ -361,18 +363,13 @@
<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>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"ប្រតិបត្តិការ"</string>
+ <string name="user_interface" msgid="3712869377953950887">"ផ្ទៃប៉ះ"</string>
+ <string name="thermal" msgid="6758074791325414831">"កម្ដៅ"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"មុខងារប្រើដៃម្ខាង"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ឧបករណ៍ជំនួយការស្ដាប់"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"សកម្ម"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"បានផ្ដាច់"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ឧបករណ៍ជំនួយការស្ដាប់"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ផ្គូផ្គងឧបករណ៍ថ្មី"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ចុច ដើម្បីផ្គូផ្គងឧបករណ៍ថ្មី"</string>
@@ -1201,7 +1198,7 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi នឹងមិនភ្ជាប់ដោយស្វ័យប្រវត្តិក្នុងពេលនេះទេ"</string>
<string name="see_all_networks" msgid="3773666844913168122">"មើលទាំងអស់"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ដើម្បីប្ដូរបណ្ដាញ សូមផ្ដាច់អ៊ីសឺរណិត"</string>
- <string name="wifi_scan_notify_message" msgid="3753839537448621794">"ដើម្បីធ្វើឱ្យបទពិសោធន៍ប្រើប្រាស់ឧបករណ៍ប្រសើរឡើង កម្មវិធី និងសេវាកម្មនៅតែអាចស្កេនរកបណ្ដាញ Wi‑Fi បានគ្រប់ពេល ទោះបីជានៅពេលដែលបិទ Wi‑Fi ក៏ដោយ។ អ្នកអាចប្ដូរវាបាននៅក្នុងការកំណត់ការស្កេន Wi‑Fi។ "<annotation id="link">"ប្ដូរ"</annotation></string>
+ <string name="wifi_scan_notify_message" msgid="3753839537448621794">"ដើម្បីធ្វើឱ្យបទពិសោធប្រើប្រាស់ឧបករណ៍ប្រសើរឡើង កម្មវិធី និងសេវាកម្មនៅតែអាចស្កេនរកបណ្ដាញ Wi‑Fi បានគ្រប់ពេល ទោះបីជានៅពេលដែលបិទ Wi‑Fi ក៏ដោយ។ អ្នកអាចប្ដូរវាបាននៅក្នុងការកំណត់ការស្កេន Wi‑Fi។ "<annotation id="link">"ប្ដូរ"</annotation></string>
<string name="turn_off_airplane_mode" msgid="8425587763226548579">"បិទមុខងារពេលជិះយន្តហោះ"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ចង់បញ្ចូលប្រអប់ខាងក្រោមទៅក្នុងការកំណត់រហ័ស"</string>
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"បញ្ចូលប្រអប់"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"បត់"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"លា"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"ថ្មប៊ិក <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ភ្ជាប់ប៊ិករបស់អ្នកជាមួយឆ្នាំងសាក"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"ថ្មប៊ិកនៅសល់តិច"</string>
<string name="video_camera" msgid="7654002575156149298">"កាមេរ៉ាវីដេអូ"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ផ្លូវកាត់ការស្វែងរក"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"រូបតំណាង \"បង្រួម\""</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"រូបតំណាង \"ពង្រីក\""</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ឬ"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ពន្លឺក្រោយក្ដារចុច"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"កម្រិតទី %1$d នៃ %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ការគ្រប់គ្រងផ្ទះ"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 92a8b6b..31aa875 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ನಾಳೆ ಪುನಃ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಆನ್ ಮಾಡಿ"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ಕ್ವಿಕ್ ಶೇರ್ ಮತ್ತು Find My Device ನಂತಹ ಫೀಚರ್ಗಳು ಬ್ಲೂಟೂತ್ ಅನ್ನು ಬಳಸುತ್ತವೆ"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ಬ್ಲೂಟೂತ್ ನಾಳೆ ಬೆಳಗ್ಗೆ ಆನ್ ಆಗುತ್ತದೆ"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ಆಡಿಯೋ ಹಂಚಿಕೊಳ್ಳುವಿಕೆ"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ಆಡಿಯೋವನ್ನು ಹಂಚಿಕೊಳ್ಳಲಾಗುತ್ತಿದೆ"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ಬ್ಯಾಟರಿ"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ಆಡಿಯೋ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ಹೆಡ್ಸೆಟ್"</string>
@@ -361,24 +363,19 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ಸಾಧನ ಬಳಸುವಾಗ ನೀವು ಯಾವ ರೀತಿಯ ಸಮಸ್ಯೆ ಎದುರಿಸುತ್ತೀರಿ?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ಸಮಸ್ಯೆಯ ಪ್ರಕಾರವನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡ್"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"ಪರ್ಫಾರ್ಮೆನ್ಸ್"</string>
+ <string name="user_interface" msgid="3712869377953950887">"ಬಳಕೆದಾರ ಇಂಟರ್ಫೇಸ್"</string>
+ <string name="thermal" msgid="6758074791325414831">"ಥರ್ಮಲ್"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ಒಂದು ಕೈ ಮೋಡ್"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ಶ್ರವಣ ಸಾಧನಗಳು"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ಸಕ್ರಿಯವಾಗಿದೆ"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ಡಿಸ್ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ಶ್ರವಣ ಸಾಧನಗಳು"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ಹೊಸ ಸಾಧನವನ್ನು ಪೇರ್ ಮಾಡಿ"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ಹೊಸ ಸಾಧನವನ್ನು ಜೋಡಿಸಲು ಕ್ಲಿಕ್ ಮಾಡಿ"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"ಪ್ರಿಸೆಟ್ ಅನ್ನು ಅಪ್ಡೇಟ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"ಪ್ರಿಸೆಟ್"</string>
- <string name="live_caption_title" msgid="8916875614623730005">"ಲೈವ್ ಶೀರ್ಷಿಕೆ"</string>
+ <string name="live_caption_title" msgid="8916875614623730005">"ಲೈವ್ ಕ್ಯಾಪ್ಶನ್"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ಸಾಧನದ ಮೈಕ್ರೋಫೋನ್ ನಿರ್ಬಂಧವನ್ನು ತೆಗೆಯಬೇಕೆ?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ಸಾಧನದ ಕ್ಯಾಮರಾ ನಿರ್ಬಂಧವನ್ನು ತೆಗೆಯಬೇಕೆ?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ಸಾಧನದ ಕ್ಯಾಮರಾ ಮತ್ತು ಮೈಕ್ರೋಫೋನ್ ಅನ್ನು ಅನ್ಬ್ಲಾಕ್ ಮಾಡಬೇಕೇ?"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ಫೋಲ್ಡ್ ಮಾಡಿರುವುದು"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ಅನ್ಫೋಲ್ಡ್ ಮಾಡಿರುವುದು"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"ಸ್ಟೈಲಸ್ ಬ್ಯಾಟರಿ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ನಿಮ್ಮ ಸ್ಟೈಲಸ್ ಅನ್ನು ಚಾರ್ಜರ್ಗೆ ಕನೆಕ್ಟ್ ಮಾಡಿ"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"ಸ್ಟೈಲಸ್ ಬ್ಯಾಟರಿ ಕಡಿಮೆಯಿದೆ"</string>
<string name="video_camera" msgid="7654002575156149298">"ವೀಡಿಯೊ ಕ್ಯಾಮರಾ"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ಹುಡುಕಾಟದ ಶಾರ್ಟ್ಕಟ್ಗಳು"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ಕುಗ್ಗಿಸುವ ಐಕಾನ್"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ವಿಸ್ತೃತಗೊಳಿಸುವ ಐಕಾನ್"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ಅಥವಾ"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ಕೀಬೋರ್ಡ್ ಬ್ಯಾಕ್ಲೈಟ್"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d ರಲ್ಲಿ %1$d ಮಟ್ಟ"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ಮನೆ ನಿಯಂತ್ರಣಗಳು"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index b141b48..91266b7 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"내일 다시 자동으로 사용 설정"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share, 내 기기 찾기 등의 기능에서 블루투스를 사용합니다."</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"블루투스가 내일 아침에 켜집니다."</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"오디오 공유"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"오디오 공유 중"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"오디오"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"헤드셋"</string>
@@ -361,18 +363,13 @@
<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>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"성능"</string>
+ <string name="user_interface" msgid="3712869377953950887">"사용자 인터페이스"</string>
+ <string name="thermal" msgid="6758074791325414831">"열화상"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"한 손 사용 모드"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"청각 보조 기기"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"활성"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"연결 끊김"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"청각 보조 기기"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"새 기기와 페어링"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"새 기기와 페어링하려면 클릭하세요"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"접은 상태"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"펼친 상태"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"스타일러스 배터리 <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"스타일러스를 충전기에 연결하세요"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"스타일러스 배터리 부족"</string>
<string name="video_camera" msgid="7654002575156149298">"비디오 카메라"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"검색 바로가기"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"접기 아이콘"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"확장 아이콘"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"또는"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"키보드 백라이트"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d단계 중 %1$d단계"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"홈 컨트롤"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index dc7227e..a3fd0a2 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Эртең автоматтык түрдө кайра күйгүзүү"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth Тез бөлүшүү жана Түзмөгүм кайда? сыяктуу функцияларда колдонулат"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth эртең таңда күйөт"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Чогуу угуу"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Чогуу угулууда"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батареянын деңгээли <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string>
@@ -361,18 +363,13 @@
<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>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Иштин майнаптуулугу"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Колдонуучунун интерфейси"</string>
+ <string name="thermal" msgid="6758074791325414831">"Жылуулук"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Бир кол режими"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Угуу аппараттары"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Жигердүү"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Ажыратылды"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Угуу аппараттары"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Жаңы түзмөк кошуу"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Жаңы түзмөк кошуу үчүн басыңыз"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"бүктөлгөн"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ачылган"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Стилустун батареясы: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Стилусту кубаттаңыз"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Стилустун батареясы отурайын деп калды"</string>
<string name="video_camera" msgid="7654002575156149298">"Видео камера"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Ыкчам баскычтарды издөө"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Жыйыштыруу сүрөтчөсү"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Жайып көрсөтүү сүрөтчөсү"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"же"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Баскычтоптун жарыгы"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d ичинен %1$d-деңгээл"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Үйдөгү түзмөктөрдү тескөө"</string>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index aea79e8..235015b 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -34,7 +34,7 @@
<dimen name="volume_row_slider_height">128dp</dimen>
<!-- width of ImmersiveModeConfirmation (-1 for match_parent) -->
- <dimen name="immersive_mode_cling_width">380dp</dimen>
+ <dimen name="immersive_mode_cling_width">500dp</dimen>
<dimen name="controls_activity_view_top_offset">25dp</dimen>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 5894084..31182fd 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ເປີດໃຊ້ໂດຍອັດຕະໂນມັດອີກຄັ້ງມື້ອື່ນ"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ຄຸນສົມບັດຕ່າງໆໃຊ້ Bluetooth ເຊັ່ນ: ການແຊຣ໌ດ່ວນ ແລະ ຊອກຫາອຸປະກອນຂອງຂ້ອຍ"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ຈະເປີດມື້ອື່ນເຊົ້າ"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ການແບ່ງປັນສຽງ"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ກຳລັງແບ່ງປັນສຽງ"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"ແບັດເຕີຣີ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ສຽງ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ຊຸດຫູຟັງ"</string>
@@ -361,18 +363,13 @@
<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>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"ປະສິດທິພາບ"</string>
+ <string name="user_interface" msgid="3712869377953950887">"ສ່ວນຕິດຕໍ່ຜູ້ໃຊ້"</string>
+ <string name="thermal" msgid="6758074791325414831">"ຄວາມຮ້ອນ"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ໂໝດມືດຽວ"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ອຸປະກອນຊ່ວຍຟັງ"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ນຳໃຊ້ຢູ່"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ຕັດການເຊື່ອມຕໍ່ແລ້ວ"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ອຸປະກອນຊ່ວຍຟັງ"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ຈັບຄູ່ອຸປະກອນໃໝ່"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ຄລິກເພື່ອຈັບຄູ່ອຸປະກອນໃໝ່"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ພັບແລ້ວ"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ກາງອອກແລ້ວ"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"ແບັດເຕີຣີປາກກາ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ເຊື່ອມຕໍ່ປາກກາຂອງທ່ານກັບສາຍສາກ"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"ແບັດເຕີຣີປາກກາເຫຼືອໜ້ອຍ"</string>
<string name="video_camera" msgid="7654002575156149298">"ກ້ອງວິດີໂອ"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ທາງລັດການຊອກຫາ"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ໄອຄອນຫຍໍ້ລົງ"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ໄອຄອນຂະຫຍາຍ"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ຫຼື"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ໄຟປຸ່ມແປ້ນພິມ"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"ລະດັບທີ %1$d ຈາກ %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ການຄວບຄຸມເຮືອນ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 2c9eed8..62c2911 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatiškai vėl įjungti rytoj"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Tokioms funkcijoms kaip „Spartusis bendrinimas“ ir „Rasti įrenginį“ naudojamas „Bluetooth“ ryšys"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"„Bluetooth“ ryšys bus įjungtas rytoj ryte"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Garso įrašų bendrinimas"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Bendrinamas garso įrašas"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akumuliatorius: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Garsas"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Virtualiosios realybės įrenginys"</string>
@@ -361,18 +363,13 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Kuri įrenginio funkcija buvo paveikta?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Pasirinkite problemos tipą"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekrano įrašas"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Našumas"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Naudotojo sąsaja"</string>
+ <string name="thermal" msgid="6758074791325414831">"Šiluminis"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Vienos rankos režimas"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Klausos įrenginiai"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktyvus"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Atjungtas"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Klausos įrenginiai"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Susieti naują įrenginį"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Spustelėkite, kad susietumėte naują įrenginį"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"sulenkta"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"nesulenkta"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Liko rašiklio akumuliatoriaus energijos: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Prijunkite rašiklį prie kroviklio"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Senka rašiklio akumuliatorius"</string>
<string name="video_camera" msgid="7654002575156149298">"Vaizdo kamera"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Paieškos šaukiniai"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Sutraukimo piktograma"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Išskleidimo piktograma"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"arba"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatūros foninis apšvietimas"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d lygis iš %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Namų sistemos valdymas"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 6a5d068..8d0b43c 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automātiski atkal ieslēgt rīt"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Tādas funkcijas kā “Ātrā kopīgošana” un “Atrast ierīci” izmanto Bluetooth savienojumu"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth savienojums tiks ieslēgts rīt no rīta"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Kopīgot audio"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Notiek audio kopīgošana"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akumulators: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Austiņas"</string>
@@ -361,18 +363,13 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Kuras ierīces funkcijas tika ietekmētas?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Atlasiet problēmas veidu"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekrāna ierakstīšana"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Veiktspēja"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Lietotāja saskarne"</string>
+ <string name="thermal" msgid="6758074791325414831">"Ierīces temperatūra"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Vienas rokas režīms"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Dzirdes aparāti"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktīvs"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Atvienots"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Dzirdes aparāti"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Savienot pārī jaunu ierīci"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Noklikšķiniet, lai savienotu pārī jaunu ierīci"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"aizvērta"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"atvērta"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Skārienekrāna pildspalvas akumulatora uzlādes līmenis: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Pievienojiet skārienekrāna pildspalvu lādētājam"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Zems skārienekrāna pildspalvas akumulatora līmenis"</string>
<string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Meklēšanas saīsnes"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Sakļaušanas ikona"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Izvēršanas ikona"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"vai"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastatūras fona apgaismojums"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Līmenis numur %1$d, kopā ir %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Mājas kontrolierīces"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 7c41096..1c889e6 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Автоматски вклучи повторно утре"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Функциите како „Брзо споделување“ и „Најди го мојот уред“ користат Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ќе се вклучи утре наутро"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Споделување аудио"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Се споделува аудио"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батерија: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалки"</string>
@@ -301,7 +303,7 @@
<string name="quick_settings_user_title" msgid="8673045967216204537">"Корисник"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_internet_label" msgid="6603068555872455463">"Интернет"</string>
- <string name="quick_settings_networks_available" msgid="1875138606855420438">"Мрежите се достапни"</string>
+ <string name="quick_settings_networks_available" msgid="1875138606855420438">"Достапни се мрежи"</string>
<string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Не се достапни мрежи"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"Нема достапни Wi-Fi мрежи"</string>
<string name="quick_settings_wifi_secondary_label_transient" msgid="7501659015509357887">"Се вклучува…"</string>
@@ -361,18 +363,13 @@
<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>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Изведба"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Кориснички интерфејс"</string>
+ <string name="thermal" msgid="6758074791325414831">"Термално"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим со една рака"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слушни апарати"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Активно"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Не е поврзано"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слушни апарати"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Спари нов уред"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Кликнете за да спарите нов уред"</string>
@@ -689,8 +686,8 @@
<string name="notification_automatic_title" msgid="3745465364578762652">"Автоматски"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"Без звук или вибрации"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"Без звук или вибрации и се појавува подолу во делот со разговори"</string>
- <string name="notification_channel_summary_default" msgid="777294388712200605">"Може да ѕвони или вибрира во зависност од поставките за уредот"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Може да ѕвони или вибрира во зависност од поставките на уредот. Стандардно, разговорите од <xliff:g id="APP_NAME">%1$s</xliff:g> се во балончиња."</string>
+ <string name="notification_channel_summary_default" msgid="777294388712200605">"Може да ѕвони или да вибрира во зависност од поставките за уредот"</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Може да ѕвони или да вибрира во зависност од поставките за уредот. Стандардно, разговорите од <xliff:g id="APP_NAME">%1$s</xliff:g> се во балончиња."</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Дозволете системот да определи дали известувањево треба да испушти звук или да вибрира"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Статус:</b> поставено на „Стандардно“"</string>
<string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Статус:</b> намалено на „Тивко“"</string>
@@ -1210,7 +1207,7 @@
<string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{Активна е # апликација}one{Активни се # апликација}other{Активни се # апликации}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Нови информации"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Активни апликации"</string>
- <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Овие апликации се активни и работат, дури и кога не ги користите. Ова ја подобрува нивната функционалност, но може да влијае и на траењето на батеријата."</string>
+ <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Овие апликации се активни и работат дури и кога не ги користите. Ова ја подобрува нивната функционалност, но може и да влијае на траењето на батеријата."</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Запри"</string>
<string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Запрено"</string>
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Готово"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"затворен"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"отворен"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Батерија на пенкало: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Поврзете го пенкалото со полнач"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Слаба батерија на пенкало"</string>
<string name="video_camera" msgid="7654002575156149298">"Видеокамера"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Кратенки за пребарување"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за собирање"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за проширување"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Осветлување на тастатура"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Ниво %1$d од %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Контроли за домот"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 540358c..3dd91f7 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"നാളെ വീണ്ടും സ്വയമേവ ഓണാക്കുക"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ക്വിക്ക് ഷെയർ, Find My Device പോലുള്ള ഫീച്ചറുകൾ Bluetooth ഉപയോഗിക്കുന്നു"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth നാളെ രാവിലെ ഓണാകും"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ഓഡിയോ പങ്കിടൽ"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ഓഡിയോ പങ്കിടുന്നു"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ബാറ്ററി"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ഓഡിയോ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ഹെഡ്സെറ്റ്"</string>
@@ -361,18 +363,13 @@
<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>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"പ്രകടനം"</string>
+ <string name="user_interface" msgid="3712869377953950887">"ഉപയോക്തൃ ഇന്റർഫേസ്"</string>
+ <string name="thermal" msgid="6758074791325414831">"തെർമൽ"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ഒറ്റക്കൈ മോഡ്"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"കേൾവിക്കുള്ള ഉപകരണങ്ങൾ"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"സജീവം"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"വിച്ഛേദിച്ചിരിക്കുന്നു"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"കേൾവിക്കുള്ള ഉപകരണങ്ങൾ"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"പുതിയ ഉപകരണം ജോടിയാക്കുക"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"പുതിയ ഉപകരണം ജോടിയാക്കാൻ ക്ലിക്ക് ചെയ്യുക"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ഫോൾഡ് ചെയ്തത്"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"അൺഫോൾഡ് ചെയ്തത്"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"സ്റ്റൈലസ് ബാറ്ററി <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"നിങ്ങളുടെ സ്റ്റൈലസ് ചാർജറുമായി കണക്റ്റ് ചെയ്യുക"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"സ്റ്റൈലസിന്റെ ബാറ്ററി ചാർജ് കുറവാണ്"</string>
<string name="video_camera" msgid="7654002575156149298">"വീഡിയോ ക്യാമറ"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"തിരയൽ കുറുക്കുവഴികൾ"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ചുരുക്കൽ ഐക്കൺ"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"വികസിപ്പിക്കൽ ഐക്കൺ"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"അല്ലെങ്കിൽ"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"കീബോഡ് ബാക്ക്ലൈറ്റ്"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d-ൽ %1$d-ാമത്തെ ലെവൽ"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ഹോം കൺട്രോളുകൾ"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 47e6936..5539263 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Маргааш автоматаар дахин асаах"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Түргэн хуваалцах, Миний төхөөрөмжийг олох зэрэг онцлогууд Bluetooth-г ашигладаг"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth-г маргааш өглөө асаана"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Аудио хуваалцах"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Аудио хуваалцаж байна"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> батарей"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Чихэвч"</string>
@@ -361,18 +363,13 @@
<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>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Гүйцэтгэл"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Хэрэглэгчийн интерфейс"</string>
+ <string name="thermal" msgid="6758074791325414831">"Дулааны"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Нэг гарын горим"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Сонсголын төхөөрөмжүүд"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Идэвхтэй"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Салсан"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Сонсголын төхөөрөмжүүд"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Шинэ төхөөрөмж хослуулах"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Шинэ төхөөрөмж хослуулахын тулд товшино уу"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"эвхсэн"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"дэлгэсэн"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Мэдрэгч үзгийн батарей <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Мэдрэгч үзгээ цэнэглэгчтэй холбоорой"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Мэдрэгч үзэгний батарей бага байна"</string>
<string name="video_camera" msgid="7654002575156149298">"Видео камер"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Товчлолууд хайх"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Хураах дүрс тэмдэг"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Дэлгэх дүрс тэмдэг"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"эсвэл"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Гарын арын гэрэл"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d-с %1$d-р түвшин"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Гэрийн удирдлага"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 39f1413..aea4d5a 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"उद्या पुन्हा आपोआप सुरू करा"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"क्विक शेअर आणि Find My Device यांसारखी वैशिष्ट्ये ब्लूटूथ वापरतात"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ब्लूटूथ उद्या सकाळी सुरू होईल"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ऑडिओ शेअरिंग"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ऑडिओ शेअर करत आहे"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> बॅटरी"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ऑडिओ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string>
@@ -366,10 +368,8 @@
<string name="thermal" msgid="6758074791325414831">"थर्मल"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"एकहाती मोड"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"श्रवणयंत्रे"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ॲक्टिव्ह आहे"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"डिस्कनेक्ट केले आहे"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"श्रवणयंत्रे"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"नवीन डिव्हाइस पेअर करा"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"नवीन डिव्हाइस पेअर करण्यासाठी क्लिक करा"</string>
@@ -1324,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"शोधण्यासाठी शॉर्टकट"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"कोलॅप्स करा आयकन"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"विस्तार करा आयकन"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"किंवा"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"कीबोर्ड बॅकलाइट"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d पैकी %1$d पातळी"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"होम कंट्रोल"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 8fce993..0131098 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Dihidupkan lagi esok secara automatik"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Ciri seperti Quick Share dan Find My Device menggunakan Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth akan dihidupkan esok pagi"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Perkongsian Audio"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Berkongsi Audio"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> bateri"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Set Kepala"</string>
@@ -361,18 +363,13 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Pengalaman peranti yang manakah yang terjejas?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Pilih jenis masalah"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Rakam skrin"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Prestasi"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Antara Muka Pengguna"</string>
+ <string name="thermal" msgid="6758074791325414831">"Terma"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mod sebelah tangan"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Peranti pendengaran"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktif"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Diputuskan sambungan"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Peranti pendengaran"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Gandingkan peranti baharu"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik untuk menggandingkan peranti baharu"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"terlipat"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"tidak terlipat"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateri stilus <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Sambungkan stilus anda kepada pengecas"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Bateri stilus lemah"</string>
<string name="video_camera" msgid="7654002575156149298">"Kamera video"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pintasan carian"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Kuncupkan ikon"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Kembangkan ikon"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"atau"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Cahaya latar papan kekunci"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Tahap %1$d daripada %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kawalan Rumah"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 9264261..9b19b62 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"မနက်ဖြန် အလိုအလျောက် ပြန်ဖွင့်ရန်"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"‘အမြန် မျှဝေပါ’ နှင့် Find My Device ကဲ့သို့ တူးလ်များသည် ဘလူးတုသ်သုံးသည်"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"မနက်ဖြန်နံနက်တွင် ဘလူးတုသ် ပွင့်ပါမည်"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"အော်ဒီယို မျှဝေခြင်း"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"အော်ဒီယို မျှဝေနေသည်"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ဘက်ထရီ"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"အသံ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"မိုက်ခွက်ပါနားကြပ်"</string>
@@ -361,18 +363,13 @@
<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>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"စွမ်းဆောင်ရည်"</string>
+ <string name="user_interface" msgid="3712869377953950887">"သုံးသူအတွက် ကြားခံစနစ်"</string>
+ <string name="thermal" msgid="6758074791325414831">"အပူဓာတ်"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"လက်တစ်ဖက်သုံးမုဒ်"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"နားကြားကိရိယာ"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"သုံးနေသည်"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ချိတ်ဆက်မထားပါ"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"နားကြားကိရိယာ"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"စက်အသစ်တွဲချိတ်ရန်"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"စက်အသစ် တွဲချိတ်ရန် နှိပ်ပါ"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ခေါက်ထားသည်"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ဖြန့်ထားသည်"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"စတိုင်လပ်စ် ဘက်ထရီ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"စတိုင်လပ်စ်ကို အားသွင်းကိရိယာနှင့် ချိတ်ဆက်ခြင်း"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"စတိုင်လပ်စ် ဘက်ထရီ အားနည်းနေသည်"</string>
<string name="video_camera" msgid="7654002575156149298">"ဗီဒီယိုကင်မရာ"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ရှာဖွေစာလုံး ဖြတ်လမ်း"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"လျှော့ပြရန် သင်္ကေတ"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ပိုပြရန် သင်္ကေတ"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"သို့မဟုတ်"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ကီးဘုတ်နောက်မီး"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"အဆင့် %2$d အနက် %1$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"အိမ်ထိန်းချုပ်မှုများ"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 9c2193e..a8befb3 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Slå på igjen i morgen automatisk"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funksjoner som Quick Share og Finn enheten min bruker Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth slås på i morgen tidlig"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Lyddeling"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Deler lyd"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Lyd"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Hodetelefoner"</string>
@@ -361,18 +363,13 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Hvilken del av enhetsopplevelsen din ble påvirket?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Velg problemtype"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skjermopptak"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Ytelse"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Brukergrensesnitt"</string>
+ <string name="thermal" msgid="6758074791325414831">"Termisk"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enhåndsmodus"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Høreapparater"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktiv"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Frakoblet"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Høreapparater"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Koble til en ny enhet"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klikk for å koble til en ny enhet"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"lagt sammen"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"åpen"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batteri i pekepennen: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Koble pekepennen til en lader"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Det er lite batteri i pekepennen"</string>
<string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Snarveier til søk"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Skjul-ikon"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Vis-ikon"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Bakgrunnslys for tastatur"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivå %1$d av %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Hjemkontroller"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 94b197b..c26ea3e 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"भोलि फेरि स्वतः अन गर्नुहोस्"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"क्विक सेयर र Find My Device जस्ता सुविधाहरू प्रयोग गर्न ब्लुटुथ चाहिन्छ"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ब्लुटुथ भोलि बिहान अन हुने छ"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"अडियो सेयरिङ"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"अडियो सेयर गरिँदै छ"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ब्याट्री"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"अडियो"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string>
@@ -361,18 +363,13 @@
<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>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"पर्फर्मेन्स"</string>
+ <string name="user_interface" msgid="3712869377953950887">"युजर इन्टरफेस"</string>
+ <string name="thermal" msgid="6758074791325414831">"थर्मल"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"एक हाते मोड"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"हियरिङ डिभाइसहरू"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"सक्रिय छ"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"डिस्कनेक्ट गरिएको छ"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"हियरिङ डिभाइसहरू"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"नयाँ डिभाइस कनेक्ट गर्नुहोस्"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"नयाँ डिभाइसमा कनेक्ट गर्न क्लिक गर्नुहोस्"</string>
@@ -382,9 +379,9 @@
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"डिभाइसको माइक्रोफोन अनब्लक गर्ने हो?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"डिभाइसको क्यामेरा अनब्लक गर्ने हो?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"डिभाइसको क्यामेरा र माइक्रोफोन अनब्लक गर्ने हो?"</string>
- <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"यसो गर्नुभयो भने माइक्रोफोन प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाहरूका लागि सो अनुमति अनब्लक गरिन्छ।"</string>
- <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"यसो गर्नुभयो भने क्यामेरा प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाहरूका लागि सो अनुमति अनब्लक गरिन्छ।"</string>
- <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"यसो गर्नुभयो भने क्यामेरा वा माइक्रोफोन प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाहरूका लागि सो अनुमति अनब्लक गरिन्छ।"</string>
+ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"यसो गर्नुभयो भने माइक्रोफोन प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाको हकमा यो अनुमति अनब्लक गरिन्छ।"</string>
+ <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"यसो गर्नुभयो भने क्यामेरा प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाको हकमा यो अनुमति अनब्लक गरिन्छ।"</string>
+ <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"यसो गर्नुभयो भने क्यामेरा वा माइक्रोफोन प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाको हकमा यो अनुमति अनब्लक गरिन्छ।"</string>
<string name="sensor_privacy_start_use_mic_blocked_dialog_title" msgid="2640140287496469689">"माइक्रोफोन ब्लक गरिएको छ"</string>
<string name="sensor_privacy_start_use_camera_blocked_dialog_title" msgid="7398084286822440384">"क्यामेरा ब्लक गरिएको छ"</string>
<string name="sensor_privacy_start_use_mic_camera_blocked_dialog_title" msgid="195236134743281973">"माइक र क्यामेरा ब्लक गरिएको छ"</string>
@@ -932,7 +929,7 @@
<string name="qs_dnd_prompt_auto_rule_app" msgid="1841469944118486580">"कुनै स्वचालित नियम वा एपले बाधा नपुऱ्याउनुहोस् नामक विकल्पलाई सक्रिय गऱ्यो।"</string>
<string name="running_foreground_services_title" msgid="5137313173431186685">"पृष्ठभूमिमा चल्ने एपहरू"</string>
<string name="running_foreground_services_msg" msgid="3009459259222695385">"ब्याट्री र डेटाका प्रयोग सम्बन्धी विवरणहरूका लागि ट्याप गर्नुहोस्"</string>
- <string name="mobile_data_disable_title" msgid="5366476131671617790">"मोबाइल डेटा निष्क्रिय पार्ने हो?"</string>
+ <string name="mobile_data_disable_title" msgid="5366476131671617790">"मोबाइल डेटा अफ गर्ने हो?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"तपाईं <xliff:g id="CARRIER">%s</xliff:g> मार्फत डेटा वा इन्टरनेट प्रयोग गर्न सक्नुहुने छैन। Wi-Fi मार्फत मात्र इन्टरनेट उपलब्ध हुने छ।"</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"तपाईंको सेवा प्रदायक"</string>
<string name="auto_data_switch_disable_title" msgid="5146527155665190652">"फेरि <xliff:g id="CARRIER">%s</xliff:g> को मोबाइल डेटा अन गर्ने हो?"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"फोल्ड गरिएको"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"अनफोल्ड गरिएको"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"स्टाइलसको ब्याट्री <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"आफ्नो स्टाइलस चार्जरमा कनेक्ट गर्नुहोस्"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"स्टाइलसको ब्याट्री लो छ"</string>
<string name="video_camera" msgid="7654002575156149298">"भिडियो क्यामेरा"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"खोजका सर्टकटहरू"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"\"कोल्याप्स गर्नुहोस्\" आइकन"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"\"एक्स्पान्ड गर्नुहोस्\" आइकन"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"वा"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"किबोर्ड ब्याकलाइट"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d मध्ये %1$d औँ स्तर"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"होम कन्ट्रोलहरू"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 8cdbf61..3eba8a8 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Morgen weer automatisch aanzetten"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Functies zoals Quick Share en Vind mijn apparaat gebruiken bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth wordt morgenochtend aangezet"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio delen"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audio delen…"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batterijniveau"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -366,10 +368,8 @@
<string name="thermal" msgid="6758074791325414831">"Thermisch"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Bediening met 1 hand"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hoortoestellen"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Actief"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Ontkoppeld"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hoortoestellen"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Nieuw apparaat koppelen"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik om nieuw apparaat te koppelen"</string>
@@ -1324,10 +1324,9 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Snelkoppelingen voor zoekopdrachten"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icoon voor samenvouwen"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icoon voor uitvouwen"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"of"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Achtergrondverlichting van toetsenbord"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d van %2$d"</string>
- <string name="home_controls_dream_label" msgid="6567105701292324257">"Huisbediening"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"Snel toegang tot je huisbediening als screensaver"</string>
+ <string name="home_controls_dream_label" msgid="6567105701292324257">"Bediening voor in huis"</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"Gebruik bediening voor in huis als screensaver"</string>
</resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 287d1a7..1e3ebab 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ଆସନ୍ତାକାଲି ସ୍ୱତଃ ପୁଣି ଚାଲୁ ହେବ"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share ଏବଂ Find My Device ପରି ଫିଚରଗୁଡ଼ିକ ବ୍ଲୁଟୁଥ ବ୍ୟବହାର କରେ"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ବ୍ଲୁଟୁଥ ଆସନ୍ତା କାଲି ସକାଳେ ଚାଲୁ ହେବ"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ଅଡିଓ ସେୟାରିଂ"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ଅଡିଓ ସେୟାର କରାଯାଉଛି"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ବ୍ୟାଟେରୀ"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ଅଡିଓ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ହେଡସେଟ୍"</string>
@@ -361,18 +363,13 @@
<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>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"ପରଫରମାନ୍ସ"</string>
+ <string name="user_interface" msgid="3712869377953950887">"ୟୁଜର ଇଣ୍ଟରଫେସ"</string>
+ <string name="thermal" msgid="6758074791325414831">"ଥର୍ମାଲ"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ଏକ-ହାତ ମୋଡ"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ଶ୍ରବଣ ଡିଭାଇସଗୁଡ଼ିକ"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ସକ୍ରିୟ"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ଡିସକନେକ୍ଟ ହୋଇଛି"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ଶ୍ରବଣ ଡିଭାଇସଗୁଡ଼ିକ"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ନୂଆ ଡିଭାଇସ ପେୟାର କରନ୍ତୁ"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ନୂଆ ଡିଭାଇସ ପେୟାର କରିବାକୁ କ୍ଲିକ କରନ୍ତୁ"</string>
@@ -933,7 +930,7 @@
<string name="running_foreground_services_title" msgid="5137313173431186685">"ବ୍ୟାକଗ୍ରାଉଣ୍ଡରେ ଆପ୍ ଚାଲୁଛି"</string>
<string name="running_foreground_services_msg" msgid="3009459259222695385">"ବ୍ୟାଟେରୀ ଏବଂ ଡାଟା ବ୍ୟବହାର ଉପରେ ବିବରଣୀ ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string>
<string name="mobile_data_disable_title" msgid="5366476131671617790">"ମୋବାଇଲ୍ ଡାଟା ବନ୍ଦ କରିବେ?"</string>
- <string name="mobile_data_disable_message" msgid="8604966027899770415">"ଡାଟା କିମ୍ବା ଇଣ୍ଟରନେଟ୍କୁ <xliff:g id="CARRIER">%s</xliff:g> ଦ୍ଵାରା ଆପଣଙ୍କର ଆକ୍ସେସ୍ ରହିବ ନାହିଁ। ଇଣ୍ଟରନେଟ୍ କେବଳ ୱାଇ-ଫାଇ ମାଧ୍ୟମରେ ଉପଲବ୍ଧ ହେବ।"</string>
+ <string name="mobile_data_disable_message" msgid="8604966027899770415">"ଡାଟା କିମ୍ବା ଇଣ୍ଟର୍ନେଟକୁ <xliff:g id="CARRIER">%s</xliff:g> ଦ୍ଵାରା ଆପଣଙ୍କର ଆକ୍ସେସ ରହିବ ନାହିଁ। ଇଣ୍ଟର୍ନେଟ କେବଳ ୱାଇ-ଫାଇ ମାଧ୍ୟମରେ ଉପଲବ୍ଧ ହେବ।"</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"ଆପଣଙ୍କ କେରିଅର୍"</string>
<string name="auto_data_switch_disable_title" msgid="5146527155665190652">"<xliff:g id="CARRIER">%s</xliff:g>କୁ ପୁଣି ସ୍ୱିଚ କରିବେ?"</string>
<string name="auto_data_switch_disable_message" msgid="5885533647399535852">"ଉପଲବ୍ଧତା ଆଧାରରେ ମୋବାଇଲ ଡାଟା ସ୍ୱତଃ ସ୍ୱିଚ ହେବ ନାହିଁ"</string>
@@ -1210,7 +1207,7 @@
<string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{#ଟି ଆପ ସକ୍ରିୟ ଅଛି}other{#ଟି ଆପ ସକ୍ରିୟ ଅଛି}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"ନୂଆ ସୂଚନା"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"ସକ୍ରିୟ ଆପ୍ସ"</string>
- <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"ଆପଣ ଏହି ଆପ୍ସକୁ ବ୍ୟବହାର କରୁନଥିଲେ ମଧ୍ୟ ସେଗୁଡ଼ିକ ସକ୍ରିୟ ରହିଥାଏ ଏବଂ ଚାଲୁଥାଏ। ଏହା ସେଗୁଡ଼ିକର କାର୍ଯ୍ୟକ୍ଷମତାକୁ ଉନ୍ନତ କରେ, କିନ୍ତୁ ଏହା ମଧ୍ୟ ବ୍ୟାଟେରୀ ଲାଇଫକୁ ପ୍ରଭାବିତ କରିପାରେ।"</string>
+ <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"ଆପଣ ଏହି ଆପ୍ସକୁ ବ୍ୟବହାର କରୁନଥିଲେ ମଧ୍ୟ ସେଗୁଡ଼ିକ ସକ୍ରିୟ ରହିଥାଏ ଏବଂ ଚାଲୁଥାଏ। ଏହା ସେଗୁଡ଼ିକର କାର୍ଯ୍ୟକ୍ଷମତାକୁ ଉନ୍ନତ କରେ, କିନ୍ତୁ ଏହା ମଧ୍ୟ ବେଟେରୀ ଲାଇଫକୁ ପ୍ରଭାବିତ କରିପାରେ।"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"ବନ୍ଦ ହୋଇଛି"</string>
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"ହୋଇଗଲା"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ଫୋଲ୍ଡେଡ"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ଅନଫୋଲ୍ଡେଡ"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"ଷ୍ଟାଇଲସ ବେଟେରୀ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ଏକ ଚାର୍ଜର ସହ ଆପଣଙ୍କ ଷ୍ଟାଇଲସକୁ କନେକ୍ଟ କରନ୍ତୁ"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"ଷ୍ଟାଇଲସ ବେଟେରୀର ଚାର୍ଜ କମ ଅଛି"</string>
<string name="video_camera" msgid="7654002575156149298">"ଭିଡିଓ କେମେରା"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ସର୍ଚ୍ଚ ସର୍ଟକଟ"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ଆଇକନକୁ ସଙ୍କୁଚିତ କରନ୍ତୁ"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ଆଇକନକୁ ବିସ୍ତାର କରନ୍ତୁ"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"କିମ୍ବା"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"କୀବୋର୍ଡ ବେକଲାଇଟ"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dରୁ %1$d ନମ୍ବର ଲେଭେଲ"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ହୋମ କଣ୍ଟ୍ରୋଲ୍ସ"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index a16a8a5..dddda72 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ਕੱਲ੍ਹ ਨੂੰ ਆਪਣੇ ਆਪ ਚਾਲੂ ਹੋ ਜਾਵੇਗਾ"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ਕਵਿੱਕ ਸ਼ੇਅਰ ਅਤੇ Find My Device ਵਰਗੀਆਂ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਬਲੂਟੁੱਥ ਵਰਤਦੀਆਂ ਹਨ"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ਬਲੂਟੁੱਥ ਕੱਲ੍ਹ ਸਵੇਰੇ ਚਾਲੂ ਹੋ ਜਾਵੇਗਾ"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ਆਡੀਓ ਸਾਂਝਾਕਰਨ"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ਆਡੀਓ ਨੂੰ ਸਾਂਝਾ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ਬੈਟਰੀ"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ਆਡੀਓ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ਹੈੱਡਸੈੱਟ"</string>
@@ -361,18 +363,13 @@
<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>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"ਕਾਰਗੁਜ਼ਾਰੀ"</string>
+ <string name="user_interface" msgid="3712869377953950887">"ਯੂਜ਼ਰ ਇੰਟਰਫ਼ੇਸ"</string>
+ <string name="thermal" msgid="6758074791325414831">"ਥਰਮਲ"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ਇੱਕ ਹੱਥ ਮੋਡ"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ਸੁਣਨ ਵਾਲੇ ਡੀਵਾਈਸ"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ਕਿਰਿਆਸ਼ੀਲ"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ਡਿਸਕਨੈਕਟ ਹੈ"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ਸੁਣਨ ਵਾਲੇ ਡੀਵਾਈਸ"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ਨਵਾਂ ਡੀਵਾਈਸ ਜੋੜਾਬੱਧ ਕਰੋ"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"\'ਨਵਾਂ ਡੀਵਾਈਸ ਜੋੜਾਬੱਧ ਕਰੋ\' \'ਤੇ ਕਲਿੱਕ ਕਰੋ"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ਫੋਲਡਯੋਗ ਡੀਵਾਈਸ"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ਅਣਫੋਲਡਯੋਗ ਡੀਵਾਈਸ"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"ਸਟਾਈਲਸ ਦੀ ਬੈਟਰੀ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ਆਪਣੇ ਸਟਾਈਲਸ ਨੂੰ ਚਾਰਜਰ ਨਾਲ ਕਨੈਕਟ ਕਰੋ"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"ਸਟਾਈਲਸ ਦੀ ਬੈਟਰੀ ਘੱਟ ਹੈ"</string>
<string name="video_camera" msgid="7654002575156149298">"ਵੀਡੀਓ ਕੈਮਰਾ"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ਖੋਜ ਸੰਬੰਧੀ ਸ਼ਾਰਟਕੱਟ"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ਪ੍ਰਤੀਕ ਨੂੰ ਸਮੇਟੋ"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ਪ੍ਰਤੀਕ ਦਾ ਵਿਸਤਾਰ ਕਰੋ"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ਜਾਂ"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ਕੀ-ਬੋਰਡ ਬੈਕਲਾਈਟ"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d ਵਿੱਚੋਂ %1$d ਪੱਧਰ"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ਹੋਮ ਕੰਟਰੋਲ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 58561f1..2dbcf20 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatycznie włącz ponownie jutro"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetootha używają funkcje takie jak szybkie udostępnianie czy Znajdź moje urządzenie"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth włączy się jutro rano"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Udostępnianie dźwięku"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Udostępniam dźwięk"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> naładowania baterii"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Dźwięk"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Zestaw słuchawkowy"</string>
@@ -366,10 +368,8 @@
<string name="thermal" msgid="6758074791325414831">"Termografia"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Tryb jednej ręki"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Urządzenia słuchowe"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktywny"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Rozłączono"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Urządzenia słuchowe"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Sparuj nowe urządzenie"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknij, aby sparować nowe urządzenie"</string>
@@ -1324,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Skróty do wyszukiwania"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona zwijania"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozwijania"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"lub"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podświetlenie klawiatury"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Poziom %1$d z %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Sterowanie domem"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 8f5c519..992db40 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ativar automaticamente de novo amanhã"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Recursos como o Quick Share e o Encontre Meu Dispositivo usam Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"O Bluetooth será ativado amanhã de manhã"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Compartilhamento de áudio"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Compartilhando áudio"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Fone de ouvido"</string>
@@ -361,18 +363,13 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Que parte da sua experiência no dispositivo foi afetada?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecionar tipo de problema"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravação de tela"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Desempenho"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Interface do usuário"</string>
+ <string name="thermal" msgid="6758074791325414831">"Térmico"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo uma mão"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Aparelhos auditivos"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ativos"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desconectados"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Aparelhos auditivos"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Parear novo dispositivo"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Clique para parear o novo dispositivo"</string>
@@ -639,7 +636,7 @@
<string name="volume_panel_hint_muted" msgid="1124844870181285320">"som desativado"</string>
<string name="volume_panel_hint_vibrate" msgid="4136223145435914132">"vibrar"</string>
<string name="media_output_label_title" msgid="872824698593182505">"Tocando <xliff:g id="LABEL">%s</xliff:g> em"</string>
- <string name="media_output_title_without_playing" msgid="3825663683169305013">"Onde o áudio vai tocar?"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Áudio definido para"</string>
<string name="media_output_title_ongoing_call" msgid="208426888064112006">"Ligando"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Sintonizador System UI"</string>
<string name="status_bar" msgid="4357390266055077437">"Barra de status"</string>
@@ -1210,7 +1207,7 @@
<string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# app está ativo}one{# apps está ativo}many{# de apps estão ativos}other{# apps estão ativos}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Nova informação"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Apps ativos"</string>
- <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Esses apps ficam ativos e em execução mesmo quando não estão em uso. Isso melhora a funcionalidade deles, mas também pode afetar a duração da bateria."</string>
+ <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Esses apps ficam ativos e em execução mesmo quando não estão em uso. Isso melhora a funcionalidade deles, mas também afeta a duração da bateria."</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Parar"</string>
<string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Parado"</string>
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Concluído"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"fechado"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"aberto"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateria da stylus em <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecte sua stylus a um carregador"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Bateria da stylus fraca"</string>
<string name="video_camera" msgid="7654002575156149298">"Filmadora"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atalhos de pesquisa"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone \"Fechar\""</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone \"Abrir\""</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz de fundo do teclado"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Automação residencial"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index df73572..fb0bbfa 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Reativar amanhã automaticamente"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funcionalidades como a Partilha rápida e o serviço Localizar o meu dispositivo usam o Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"O Bluetooth vai ser ativado amanhã de manhã"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Partilha de áudio"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"A partilhar áudio"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de bateria"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ausc. c/ mic. integ."</string>
@@ -366,10 +368,8 @@
<string name="thermal" msgid="6758074791325414831">"Térmico"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo para uma mão"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Dispositivos auditivos"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ativos"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desligados"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Dispositivos auditivos"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Sincronizar novo dispositivo"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Clique para sincronizar um novo dispositivo"</string>
@@ -930,7 +930,7 @@
<string name="running_foreground_services_title" msgid="5137313173431186685">"Apps em execução em segundo plano"</string>
<string name="running_foreground_services_msg" msgid="3009459259222695385">"Toque para obter detalhes acerca da utilização da bateria e dos dados"</string>
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Desativar os dados móveis?"</string>
- <string name="mobile_data_disable_message" msgid="8604966027899770415">"Não terá acesso a dados ou à Internet através do operador <xliff:g id="CARRIER">%s</xliff:g>. A Internet estará disponível apenas por Wi-Fi."</string>
+ <string name="mobile_data_disable_message" msgid="8604966027899770415">"Não vai ter acesso aos dados nem à Internet através do operador <xliff:g id="CARRIER">%s</xliff:g>. A Internet vai estar disponível só por Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"o seu operador"</string>
<string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Mudar de novo para <xliff:g id="CARRIER">%s</xliff:g>?"</string>
<string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Os dados móveis não vão mudar automaticamente com base na disponibilidade"</string>
@@ -1324,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atalhos de pesquisa"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone de reduzir"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone de expandir"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz do teclado"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Controlos domésticos"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 8f5c519..992db40 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ativar automaticamente de novo amanhã"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Recursos como o Quick Share e o Encontre Meu Dispositivo usam Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"O Bluetooth será ativado amanhã de manhã"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Compartilhamento de áudio"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Compartilhando áudio"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Fone de ouvido"</string>
@@ -361,18 +363,13 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Que parte da sua experiência no dispositivo foi afetada?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecionar tipo de problema"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravação de tela"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Desempenho"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Interface do usuário"</string>
+ <string name="thermal" msgid="6758074791325414831">"Térmico"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo uma mão"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Aparelhos auditivos"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ativos"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desconectados"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Aparelhos auditivos"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Parear novo dispositivo"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Clique para parear o novo dispositivo"</string>
@@ -639,7 +636,7 @@
<string name="volume_panel_hint_muted" msgid="1124844870181285320">"som desativado"</string>
<string name="volume_panel_hint_vibrate" msgid="4136223145435914132">"vibrar"</string>
<string name="media_output_label_title" msgid="872824698593182505">"Tocando <xliff:g id="LABEL">%s</xliff:g> em"</string>
- <string name="media_output_title_without_playing" msgid="3825663683169305013">"Onde o áudio vai tocar?"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Áudio definido para"</string>
<string name="media_output_title_ongoing_call" msgid="208426888064112006">"Ligando"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Sintonizador System UI"</string>
<string name="status_bar" msgid="4357390266055077437">"Barra de status"</string>
@@ -1210,7 +1207,7 @@
<string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# app está ativo}one{# apps está ativo}many{# de apps estão ativos}other{# apps estão ativos}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Nova informação"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Apps ativos"</string>
- <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Esses apps ficam ativos e em execução mesmo quando não estão em uso. Isso melhora a funcionalidade deles, mas também pode afetar a duração da bateria."</string>
+ <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Esses apps ficam ativos e em execução mesmo quando não estão em uso. Isso melhora a funcionalidade deles, mas também afeta a duração da bateria."</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Parar"</string>
<string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Parado"</string>
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Concluído"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"fechado"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"aberto"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateria da stylus em <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecte sua stylus a um carregador"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Bateria da stylus fraca"</string>
<string name="video_camera" msgid="7654002575156149298">"Filmadora"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atalhos de pesquisa"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone \"Fechar\""</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone \"Abrir\""</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz de fundo do teclado"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Automação residencial"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 366480e..ff07fe9 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Activează din nou automat mâine"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funcții precum Quick Share și Găsește-mi dispozitivul folosesc Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth se va activa mâine dimineață"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Permiterea accesului la audio"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Se permite accesul la conținutul audio"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Nivelul bateriei: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Căști"</string>
@@ -361,18 +363,13 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Ce parte a experienței pe dispozitiv a fost afectată?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selectează tipul problemei"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Înregistrarea ecranului"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Performanță"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Interfața de utilizare"</string>
+ <string name="thermal" msgid="6758074791325414831">"Termal"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modul cu o mână"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Aparate auditive"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Activ"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Deconectat"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Aparate auditive"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Asociază un nou dispozitiv"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Dă clic pentru a asocia un nou dispozitiv"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"închis"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"deschis"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateria creionului: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conectează-ți creionul la un încărcător"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Nivelul bateriei creionului este scăzut"</string>
<string name="video_camera" msgid="7654002575156149298">"Cameră video"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Comenzi directe de căutare"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Pictograma de restrângere"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Pictograma de extindere"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"sau"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Iluminarea din spate a tastaturii"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivelul %1$d din %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Comenzi pentru locuință"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index f48e428..1900a8e 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Включить завтра автоматически"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth используется в таких функциях и сервисах, как \"Быстрая отправка\" и \"Найти устройство\""</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth включится завтра утром"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Отправка аудио"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Включена отправка аудио"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Заряд: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудиоустройство"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string>
@@ -361,18 +363,13 @@
<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>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Производительность"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Интерфейс"</string>
+ <string name="thermal" msgid="6758074791325414831">"Тепловизор"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим управления одной рукой"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слуховые аппараты"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Активно"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Не подключено"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слуховые аппараты"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Подключить новое устройство"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Нажмите, чтобы подключить новое устройство"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"устройство сложено"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"устройство разложено"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Батарея стилуса: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Поставьте стилус на зарядку."</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Низкий заряд батареи стилуса"</string>
<string name="video_camera" msgid="7654002575156149298">"Видеокамера"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Найти быстрые клавиши"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок \"Свернуть\""</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок \"Развернуть\""</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Подсветка клавиатуры"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Уровень %1$d из %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Управление домом"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 0e621c6..5d41eed 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"හෙට ස්වයංක්රීයව නැවත ක්රියාත්මක කරන්න"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ඉක්මන් බෙදා ගැනීම සහ මගේ උපාංගය සෙවීම වැනි විශේෂාංග බ්ලූටූත් භාවිත කරයි"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"බ්ලූටූත් හෙට උදේ සක්රීය වෙයි"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ශ්රව්ය බෙදා ගැනීම"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ශ්රව්යය බෙදා ගැනීම"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"බැටරිය <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ශ්රව්ය"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"හෙඩ්සෙටය"</string>
@@ -361,24 +363,19 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ඔබේ උපාංග අත්දැකීමේ කුමන කොටසට බලපෑවේ ද?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ගැටලු වර්ගය තෝරන්න"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"තිර පටිගත කිරීම"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"කාර්ය සාධනය"</string>
+ <string name="user_interface" msgid="3712869377953950887">"පරිශීලක අතුරු මුහුණත"</string>
+ <string name="thermal" msgid="6758074791325414831">"තාප"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"තනි අත් ප්රකාරය"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ශ්රවණ උපාංග"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ක්රියාකාරී"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"විසන්ධි විය"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ශ්රවණ උපාංග"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"නව උපාංගය යුගල කරන්න"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"නව උපාංගය යුගල කිරීමට ක්ලික් කරන්න"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"පෙර සැකසීම යාවත්කාලීන කළ නොහැකි විය"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"පෙරසැකසුම"</string>
- <string name="live_caption_title" msgid="8916875614623730005">"සජීවී සිරස්තලය"</string>
+ <string name="live_caption_title" msgid="8916875614623730005">"සජීවී සිරස්තල"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"උපාංග මයික්රෆෝනය අවහිර කිරීම ඉවත් කරන්නද?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"උපාංග කැමරාව අවහිර කිරීම ඉවත් කරන්නද?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"උපාංග කැමරාව සහ මයික්රෆෝනය අවහිර කිරීම ඉවත් කරන්නද?"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"නැවූ"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"නොනැවූ"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"පන්හිඳ බැටරිය <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ඔබේ පන්හිඳ චාජරයකට සම්බන්ධ කරන්න"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"පන්හිඳ බැටරිය අඩුයි"</string>
<string name="video_camera" msgid="7654002575156149298">"වීඩියෝ කැමරාව"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"කෙටි මං සොයන්න"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"හැකුළුම් නිරූපකය"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"දිගහැරීම් නිරූපකය"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"හෝ"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"යතුරු පුවරු පසු ආලෝකය"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dන් %1$d වැනි මට්ටම"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"නිවෙස් පාලන"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index ba72fb4..eef2a01 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automaticky zajtra znova zapnúť"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funkcie ako Quick Share a Nájdi moje zariadenie používajú Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth sa zapne zajtra ráno"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Zdieľanie zvuku"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Zdieľa sa zvuk"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batéria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Náhlavná súprava"</string>
@@ -361,18 +363,13 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Čo v zariadení bolo ovplyvnené?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Vyberte typ problému"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Rekordér obrazovky"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Výkon"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Používateľské rozhranie"</string>
+ <string name="thermal" msgid="6758074791325414831">"Termálne"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Režim jednej ruky"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Načúvacie zariadenia"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktívne"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Odpojené"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Načúvacie zariadenia"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Spárovať nové zariadenie"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknutím spárujete nové zariadenie"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"zložené"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"rozložené"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batéria dotykového pera: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Pripojte dotykové pero k nabíjačke"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Stav batérie dotykového pera je nízky"</string>
<string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
@@ -1328,10 +1324,9 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Vyhľadávacie odkazy"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona zbalenia"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozbalenia"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"alebo"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podsvietenie klávesnice"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. úroveň z %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Ovládanie domácnosti"</string>
- <string name="home_controls_dream_description" msgid="4644150952104035789">"Rýchlejšie ovládanie domácnosti v šetriči obrazov."</string>
+ <string name="home_controls_dream_description" msgid="4644150952104035789">"Rýchly prístup k ovládaniu domácnosti z šetriča obrazovky"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index c6c1439..facf72b 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Samodejno znova vklopi jutri"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funkcije, kot sta Hitro deljenje in Poišči mojo napravo, uporabljajo Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth se bo vklopil jutri zjutraj"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Deljenje zvoka"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Poteka deljenje zvoka"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterija na <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvok"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalke z mikrofonom"</string>
@@ -361,18 +363,13 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Na kateri del izkušnje z napravo je to vplivalo?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Izberite vrsto težave"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snemanje zaslona"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Učinkovitost delovanja"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Uporabniški vmesnik"</string>
+ <string name="thermal" msgid="6758074791325414831">"Toplotno"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enoročni način"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Slušni pripomočki"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivno"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Brez povezave"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Slušni pripomočki"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Seznanitev nove naprave"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite za seznanitev nove naprave"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"zaprto"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"razprto"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Napolnjenost baterije pisala: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Povežite pisalo s polnilnikom."</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Skoraj prazna baterija pisala"</string>
<string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Bližnjice za iskanje"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za strnitev"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za razširitev"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ali"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Osvetlitev tipkovnice"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Stopnja %1$d od %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrolniki za dom"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index dc9407f..5333632 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktivizoje automatikisht sërish nesër"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Veçoritë e tilla si \"Ndarja e shpejtë\" dhe \"Gjej pajisjen time\" përdorin Bluetooth-in"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth-i do të aktivizohet nesër në mëngjes"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Ndarja e audios"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audioja po ndahet"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> bateri"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Kufje me mikrofon"</string>
@@ -361,12 +363,9 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cila pjesë e përvojës me pajisjen është prekur?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Zgjidh llojin e problemit"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Regjistrim i ekranit"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Performanca"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Ndërfaqja e përdoruesit"</string>
+ <string name="thermal" msgid="6758074791325414831">"Termike"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modaliteti i përdorimit me një dorë"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Pajisje ndihmëse për dëgjimin"</string>
<!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
@@ -1275,8 +1274,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"palosur"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"shpalosur"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateria e stilolapsit: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Lidhe stilolapsin me një karikues"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Bateria e stilolapsit në nivel të ulët"</string>
<string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
@@ -1328,8 +1326,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Kërko për shkurtoret"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona e palosjes"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona e zgjerimit"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ose"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Drita e sfondit e tastierës"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveli: %1$d nga %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrollet e shtëpisë"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 1228eef..f35d7d8 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Аутоматски поново укључи сутра"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Функције као што су Quick Share и Пронађи мој уређај користе Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ће се укључити сутра ујутру"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Дељење звука"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Дели се звук"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Ниво батерије је <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалице"</string>
@@ -366,10 +368,8 @@
<string name="thermal" msgid="6758074791325414831">"Термална камера"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим једном руком"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слушни апарати"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Активно"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Веза је прекинута"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слушни апарати"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Упари нови уређај"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Кликните да бисте упарили нов уређај"</string>
@@ -1324,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Пречице претраге"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за скупљање"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за проширивање"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Позадинско осветљење тастатуре"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. ниво од %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Контроле за дом"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index cf2b93c..535a4aa 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktivera automatiskt igen i morgon"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funktioner som Snabbdelning och Hitta min enhet använder Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth aktiveras i morgon bitti"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Ljuddelning"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Delar ljud"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ljud"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -366,10 +368,8 @@
<string name="thermal" msgid="6758074791325414831">"Termisk"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enhandsläge"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hörhjälpmedel"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktiva"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Frånkopplade"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hörhjälpmedel"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Parkoppla en ny enhet"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klicka för att parkoppla en ny enhet"</string>
@@ -1324,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Sökgenvägar"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikonen Komprimera"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikonen Utöka"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Bakgrundsbelysning för tangentbord"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivå %1$d av %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Hemstyrning"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 1ee1d0c..98a7e03 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Iwashe tena kesho kiotomatiki"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Vipengele kama vile Kutuma Haraka na Tafuta Kifaa Changu hutumia Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth itawaka kesho asubuhi"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Kusikiliza Pamoja"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Mnasikiliza Pamoja"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Chaji ya betri ni <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Sauti"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Vifaa vya sauti"</string>
@@ -361,18 +363,13 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Ni sehemu gani ya matumizi ya kifaa iliathiriwa?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Chagua aina ya tatizo"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Rekodi ya skrini"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Utendaji"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Kiolesura"</string>
+ <string name="thermal" msgid="6758074791325414831">"Joto"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Hali ya kutumia kwa mkono mmoja"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Vifaa vya kusikilizia"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Vimeunganishwa"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Havijaunganishwa"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Vifaa vya kusikilizia"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Unganisha kifaa kipya"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Bofya ili uunganishe kifaa kipya"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"kimekunjwa"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"kimefunguliwa"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Betri ya stylus <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Unganisha stylus yako kwenye chaja"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Chaji ya betri ya Stylus imepungua"</string>
<string name="video_camera" msgid="7654002575156149298">"Kamera ya video"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Njia mkato za kutafutia"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Kunja aikoni"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Panua aikoni"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"au"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Mwanga chini ya kibodi"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Kiwango cha %1$d kati ya %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Dhibiti Vifaa Nyumbani"</string>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 29e0dbe..27af334 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -56,7 +56,7 @@
<dimen name="navigation_key_padding">25dp</dimen>
<!-- width of ImmersiveModeConfirmation (-1 for match_parent) -->
- <dimen name="immersive_mode_cling_width">380dp</dimen>
+ <dimen name="immersive_mode_cling_width">600dp</dimen>
<!-- Keyboard shortcuts helper -->
<dimen name="ksh_layout_width">488dp</dimen>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 6e95c3b..896b55f 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"நாளைக்குத் தானாகவே மீண்டும் இயக்கப்படுதல்"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"விரைவுப் பகிர்தல், Find My Device போன்ற அம்சங்கள் புளூடூத்தைப் பயன்படுத்துகின்றன"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"நாளை காலை புளூடூத் இயக்கப்படும்"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ஆடியோ பகிர்வு"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ஆடியோ பகிரப்படுகிறது"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> பேட்டரி"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ஆடியோ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ஹெட்செட்"</string>
@@ -305,7 +307,7 @@
<string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"நெட்வொர்க்குகள் கிடைக்கவில்லை"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"வைஃபை நெட்வொர்க்குகள் இல்லை"</string>
<string name="quick_settings_wifi_secondary_label_transient" msgid="7501659015509357887">"ஆன் செய்கிறது…"</string>
- <string name="quick_settings_cast_title" msgid="3033553249449938182">"Cast"</string>
+ <string name="quick_settings_cast_title" msgid="3033553249449938182">"அலைபரப்பு"</string>
<string name="quick_settings_casting" msgid="1435880708719268055">"அனுப்புகிறது"</string>
<string name="quick_settings_cast_device_default_name" msgid="6988469571141331700">"பெயரிடப்படாத சாதனம்"</string>
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"சாதனங்கள் இல்லை"</string>
@@ -361,18 +363,13 @@
<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>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"செயல்திறன்"</string>
+ <string name="user_interface" msgid="3712869377953950887">"பயனர் இடைமுகம்"</string>
+ <string name="thermal" msgid="6758074791325414831">"தெர்மல்"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ஒற்றைக் கைப் பயன்முறை"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"செவித்துணைக் கருவிகள்"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"செயலில் உள்ளது"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"இணைக்கப்படவில்லை"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"செவித்துணைக் கருவிகள்"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"புதிய சாதனத்தை இணை"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"புதிய சாதனத்தை இணைக்க கிளிக் செய்யலாம்"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"மடக்கப்பட்டது"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"விரிக்கப்பட்டது"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"ஸ்டைலஸ் பேட்டரி <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"உங்கள் ஸ்டைலஸைச் சார்ஜருடன் இணையுங்கள்"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"ஸ்டைலஸின் பேட்டரி குறைவாக உள்ளது"</string>
<string name="video_camera" msgid="7654002575156149298">"வீடியோ கேமரா"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"தேடல் ஷார்ட்கட்கள்"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"சுருக்குவதற்கான ஐகான்"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"விரிவாக்குவதற்கான ஐகான்"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"அல்லது"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"கீபோர்டு பேக்லைட்"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"நிலை, %2$d இல் %1$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ஹோம் கன்ட்ரோல்கள்"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 64d6f9e..b88386f 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"రేపు మళ్లీ ఆటోమేటిక్గా ఆన్ చేస్తుంది"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"క్విక్ షేర్, Find My Device వంటి ఫీచర్లు బ్లూటూత్ను ఉపయోగిస్తాయి"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"బ్లూటూత్ రేపు ఉదయం ఆన్ అవుతుంది"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ఆడియో షేరింగ్"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ఆడియోను షేర్ చేస్తున్నారు"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> బ్యాటరీ"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ఆడియో"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"హెడ్సెట్"</string>
@@ -361,18 +363,13 @@
<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>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"పనితీరు"</string>
+ <string name="user_interface" msgid="3712869377953950887">"యూజర్ ఇంటర్ఫేస్"</string>
+ <string name="thermal" msgid="6758074791325414831">"థర్మల్"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"వన్-హ్యాండెడ్ మోడ్"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"వినికిడి పరికరాలు"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"యాక్టివ్"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"డిస్కనెక్ట్ అయ్యింది"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"వినికిడి పరికరాలు"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"కొత్త పరికరాన్ని పెయిర్ చేయండి"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"కొత్త పరికరాన్ని పెయిర్ చేయడానికి క్లిక్ చేయండి"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"మడిచే సదుపాయం గల పరికరం"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"మడిచే సదుపాయం లేని పరికరం"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"స్టయిలస్ బ్యాటరీ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"మీ స్టైలస్ను ఛార్జర్కి కనెక్ట్ చేయండి"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"తక్కువ స్టైలస్ బ్యాటరీ"</string>
<string name="video_camera" msgid="7654002575156149298">"వీడియో కెమెరా"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"సెర్చ్ షార్ట్కట్లు"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"కుదించండి చిహ్నం"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"విస్తరించండి చిహ్నం"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"లేదా"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"కీబోర్డ్ బ్యాక్లైట్"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dలో %1$dవ స్థాయి"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"హోమ్ కంట్రోల్స్"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 8a76629..12c676e 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -279,10 +279,12 @@
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ยกเลิกการเชื่อมต่อ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"เปิดใช้งาน"</string>
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"เปิดอีกครั้งโดยอัตโนมัติในวันพรุ่งนี้"</string>
- <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ฟีเจอร์ต่างๆ เช่น Quick Share และหาอุปกรณ์ของฉัน ใช้บลูทูธ"</string>
+ <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ฟีเจอร์ต่างๆ เช่น Quick Share และ \"หาอุปกรณ์ของฉัน\" ใช้บลูทูธ"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"บลูทูธจะเปิดพรุ่งนี้เช้า"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"การแชร์เสียง"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"กำลังแชร์เสียง"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"แบตเตอรี่ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"เสียง"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ชุดหูฟัง"</string>
@@ -366,10 +368,8 @@
<string name="thermal" msgid="6758074791325414831">"ความร้อน"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"โหมดมือเดียว"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"เครื่องช่วยฟัง"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ใช้งานอยู่"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ยกเลิกการเชื่อมต่อแล้ว"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"เครื่องช่วยฟัง"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"จับคู่อุปกรณ์ใหม่"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"คลิกเพื่อจับคู่อุปกรณ์ใหม่"</string>
@@ -1324,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ค้นหาแป้นพิมพ์ลัด"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ไอคอนยุบ"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ไอคอนขยาย"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"หรือ"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ไฟแบ็กไลต์ของแป้นพิมพ์"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"ระดับที่ %1$d จาก %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ระบบควบคุมอุปกรณ์สมาร์ทโฮม"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index e74139f..84a4541 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Awtomatikong i-on ulit bukas"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Gumagamit ng Bluetooth ang mga feature tulad ng Quick Share at Hanapin ang Aking Device"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Mag-o-on ang Bluetooth bukas ng umaga"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Pag-share ng Audio"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Ibinabahagi ang Audio"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> na baterya"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -366,10 +368,8 @@
<string name="thermal" msgid="6758074791325414831">"Thermal"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-hand mode"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Mga hearing device"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktibo"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Nadiskonekta"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Mga hearing device"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Magpares ng bagong device"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"I-click para magpares ng bagong device"</string>
@@ -1324,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Mga shortcut ng paghahanap"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"I-collapse ang icon"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"I-expand ang icon"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Backlight ng keyboard"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d sa %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Mga Home Control"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index a66fbed..c2df2c2 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Yarın otomatik olarak tekrar aç"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share ve Cihazımı Bul gibi özellikler Bluetooth\'u kullanır"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth yarın sabah açılacak"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Ses Paylaşımı"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Ses paylaşılıyor"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Pil düzeyi <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ses"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Mikrofonlu kulaklık"</string>
@@ -361,18 +363,13 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cihaz deneyiminiz ne şekilde etkilendi?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Sorun türünü seçin"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekran kaydedicisi"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Performans"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Kullanıcı Arayüzü"</string>
+ <string name="thermal" msgid="6758074791325414831">"Termal"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Tek el modu"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"İşitme cihazları"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Etkin"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Bağlı değil"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"İşitme cihazları"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Yeni cihaz eşle"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Yeni cihaz eşlemek için tıklayın"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"katlanmış"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"katlanmamış"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Ekran kalemi pili: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Ekran kaleminizi bir şarj cihazına bağlayın"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Ekran kaleminin pil seviyesi düşük"</string>
<string name="video_camera" msgid="7654002575156149298">"Video kamera"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Arama kısayolları"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Daralt simgesi"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Genişlet simgesi"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"veya"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klavye aydınlatması"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Seviye %1$d / %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Ev Kontrolleri"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 5262d74..5fcfb3e 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Автоматично ввімкнути знову завтра"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Такі функції, як швидкий обмін і \"Знайти пристрій\", використовують Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth увімкнеться завтра вранці"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Надсилання аудіо"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Надсилання аудіо"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> заряду акумулятора"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудіопристрій"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнітура"</string>
@@ -361,18 +363,13 @@
<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>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Продуктивність"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Інтерфейс користувача"</string>
+ <string name="thermal" msgid="6758074791325414831">"Нагрівання"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим керування однією рукою"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слухові апарати"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Під’єднано"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Від’єднано"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слухові апарати"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Підключити новий пристрій"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Натисніть, щоб підключити новий пристрій"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"складений"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"розкладений"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Заряд акумулятора стилуса: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Підключіть стилус до зарядного пристрою"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Низький заряд акумулятора стилуса"</string>
<string name="video_camera" msgid="7654002575156149298">"Відеокамера"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Комбінації клавіш для пошуку"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок згортання"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок розгортання"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"або"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Підсвічування клавіатури"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Рівень %1$d з %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Автоматизація дому"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 0d3f1fe..f08dd7e 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -278,11 +278,13 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"محفوظ ہے"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"غیر منسلک کریں"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"فعال کریں"</string>
- <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"کل دوبارہ خودکار طور پر آن ہوگا"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"کل دوبارہ خودکار طور پر آن کریں"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"فوری اشتراک اور \'میرا آلہ ڈھونڈیں\' جیسی خصوصیات بلوٹوتھ کا استعمال کرتی ہیں"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"بلوٹوتھ کل صبح آن ہو جائے گا"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"آڈیو کا اشتراک"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"آڈیو کا اشتراک ہو رہا ہے"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> بیٹری"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"آڈیو"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ہیڈ سیٹ"</string>
@@ -366,10 +368,8 @@
<string name="thermal" msgid="6758074791325414831">"تھرمل"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ایک ہاتھ کی وضع"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"سماعت کے آلات"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"فعال"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"غیر منسلک ہے"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"سماعت کے آلات"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"نئے آلے کا جوڑا بنائیں"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"نئے آلے کا جوڑا بنانے کے لیے کلک کریں"</string>
@@ -1328,8 +1328,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"تلاش کے شارٹ کٹس"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"آئیکن سکیڑیں"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"آئیکن پھیلائیں"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"یا"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"کی بورڈ بیک لائٹ"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d میں سے %1$d کا لیول"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"ہوم کنٹرولز"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 3f3983d..4a04a33 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ertaga yana avtomatik yoqilsin"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Tezkor ulashuv va Qurilmamni top kabi funksiyalar Bluetooth ishlatadi"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ertaga ertalab yoqiladi"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio ulashuvi"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audio ulashuvi yoniq"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batareya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Garnitura"</string>
@@ -366,10 +368,8 @@
<string name="thermal" msgid="6758074791325414831">"Termal"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Ixcham rejim"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Eshitish qurilmalari"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Faol"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Uzildi"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Eshitish qurilmalari"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Yangi qurilmani ulash"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Yangi qurilmani ulash uchun bosing"</string>
@@ -1324,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tezkor tugmalar qidiruvi"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Yigʻish belgisi"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Yoyish belgisi"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"yoki"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatura orqa yoritkichi"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Daraja: %1$d / %2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Uy boshqaruvi"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index dc4fa12..45d9165 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Tự động bật lại vào ngày mai"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Các tính năng như Chia sẻ nhanh và Tìm thiết bị của tôi đều sử dụng Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth sẽ bật vào sáng mai"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Chia sẻ âm thanh"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Đang chia sẻ âm thanh"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> pin"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Âm thanh"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Tai nghe"</string>
@@ -361,18 +363,13 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Bạn gặp loại vấn đề gì khi dùng thiết bị?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Chọn loại vấn đề"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ghi màn hình"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Hiệu suất"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Giao diện người dùng"</string>
+ <string name="thermal" msgid="6758074791325414831">"Nhiệt"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Chế độ một tay"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Thiết bị trợ thính"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Đang hoạt động"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Đã ngắt kết nối"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Thiết bị trợ thính"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Ghép nối thiết bị mới"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Nhấp để ghép nối thiết bị mới"</string>
@@ -933,7 +930,7 @@
<string name="running_foreground_services_title" msgid="5137313173431186685">"Ứng dụng đang chạy trong nền"</string>
<string name="running_foreground_services_msg" msgid="3009459259222695385">"Nhấn để biết chi tiết về mức sử dụng dữ liệu và pin"</string>
<string name="mobile_data_disable_title" msgid="5366476131671617790">"Tắt dữ liệu di động?"</string>
- <string name="mobile_data_disable_message" msgid="8604966027899770415">"Bạn sẽ không có quyền sử dụng dữ liệu hoặc truy cập Internet thông qua chế độ <xliff:g id="CARRIER">%s</xliff:g>. Bạn chỉ có thể truy cập Internet thông qua Wi-Fi."</string>
+ <string name="mobile_data_disable_message" msgid="8604966027899770415">"Bạn sẽ không có quyền sử dụng dữ liệu hoặc truy cập Internet thông qua mạng <xliff:g id="CARRIER">%s</xliff:g>. Bạn chỉ có thể truy cập Internet thông qua Wi-Fi."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"nhà mạng của bạn"</string>
<string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Chuyển về <xliff:g id="CARRIER">%s</xliff:g>?"</string>
<string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Dữ liệu di động sẽ không tự động chuyển dựa trên tình trạng phủ sóng"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"gập"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"mở"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Mức pin bút cảm ứng <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Hãy kết nối bút cảm ứng với bộ sạc"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Bút cảm ứng bị yếu pin"</string>
<string name="video_camera" msgid="7654002575156149298">"Máy quay video"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Lối tắt tìm kiếm"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Biểu tượng Thu gọn"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Biểu tượng Mở rộng"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"hoặc"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Đèn nền bàn phím"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Độ sáng %1$d/%2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Điều khiển nhà"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 9b69f9e..98a1187 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明天自动重新开启"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"快速分享、查找我的设备等功能会使用蓝牙"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"蓝牙将在明天早上开启"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"音频分享"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"正在分享音频"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> 的电量"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音频"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳机"</string>
@@ -361,18 +363,13 @@
<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>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"性能"</string>
+ <string name="user_interface" msgid="3712869377953950887">"界面"</string>
+ <string name="thermal" msgid="6758074791325414831">"散热"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"单手模式"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"助听装置"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"已连接"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"已断开连接"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"助听装置"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"与新设备配对"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"点击即可与新设备配对"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"折叠状态"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"展开状态"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"触控笔电量为 <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"请将触控笔连接充电器"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"触控笔电池电量低"</string>
<string name="video_camera" msgid="7654002575156149298">"摄像机"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜索快捷键"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收起图标"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展开图标"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"键盘背光"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 级,共 %2$d 级"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"家居控制"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 5c164ab..edc624f 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明天自動重新開啟"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"「快速共享」和「尋找我的裝置」等功能都會使用藍牙"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"藍牙將於明天上午開啟"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"音訊分享功能"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"正在分享音訊"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"電量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音訊"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳機"</string>
@@ -361,18 +363,13 @@
<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>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"效能"</string>
+ <string name="user_interface" msgid="3712869377953950887">"使用者介面"</string>
+ <string name="thermal" msgid="6758074791325414831">"熱能"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"單手模式"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"助聽器"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"已連線"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"已中斷連線"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"助聽器"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"配對新裝置"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"㩒一下就可以配對新裝置"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"已摺疊"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"已打開"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"觸控筆電量:<xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"將觸控筆連接充電器"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"觸控筆電量不足"</string>
<string name="video_camera" msgid="7654002575156149298">"攝影機"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜尋快速鍵"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收合圖示"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展開圖示"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"鍵盤背光"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 級,共 %2$d 級"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"智能家居"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 0d50ad5..5679282 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明天自動重新開啟"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"「快速分享」和「尋找我的裝置」等功能都需要使用藍牙技術"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"藍牙會在明天早上開啟"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"音訊分享"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"正在分享音訊"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"電量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音訊"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳機"</string>
@@ -361,18 +363,13 @@
<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>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"效能"</string>
+ <string name="user_interface" msgid="3712869377953950887">"使用者介面"</string>
+ <string name="thermal" msgid="6758074791325414831">"熱成像"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"單手模式"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"助聽器"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"已連線"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"連線中斷"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"助聽器"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"配對新裝置"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"按一下即可配對新裝置"</string>
@@ -380,7 +377,7 @@
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"預設"</string>
<string name="live_caption_title" msgid="8916875614623730005">"即時字幕"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"要解除封鎖裝置麥克風嗎?"</string>
- <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"要解除封鎖裝置相機嗎?"</string>
+ <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"解除封鎖裝置相機?"</string>
<string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"要將裝置的相機和麥克風解除封鎖嗎?"</string>
<string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"執行後,具備麥克風存取權的所有應用程式和服務,都將可使用麥克風。"</string>
<string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"執行後,具備相機存取權的所有應用程式和服務,都將可使用相機。"</string>
@@ -933,7 +930,7 @@
<string name="running_foreground_services_title" msgid="5137313173431186685">"在背景執行的應用程式"</string>
<string name="running_foreground_services_msg" msgid="3009459259222695385">"輕觸即可查看電池和數據用量詳情"</string>
<string name="mobile_data_disable_title" msgid="5366476131671617790">"要關閉行動數據嗎?"</string>
- <string name="mobile_data_disable_message" msgid="8604966027899770415">"你將無法透過「<xliff:g id="CARRIER">%s</xliff:g>」使用行動數據或網際網路。你只能透過 Wi-Fi 使用網際網路。"</string>
+ <string name="mobile_data_disable_message" msgid="8604966027899770415">"你將無法透過「<xliff:g id="CARRIER">%s</xliff:g>」使用行動數據或網際網路,只能利用 Wi-Fi 上網。"</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"你的電信業者"</string>
<string name="auto_data_switch_disable_title" msgid="5146527155665190652">"要切換回「<xliff:g id="CARRIER">%s</xliff:g>」嗎?"</string>
<string name="auto_data_switch_disable_message" msgid="5885533647399535852">"行動數據不會依據可用性自動切換"</string>
@@ -1210,7 +1207,7 @@
<string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# 個應用程式正在運作}other{# 個應用程式正在運作}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"新資訊"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"運作中的應用程式"</string>
- <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"這些應用程式會在沒有使用的情況下持續運作。應用程式的實用度會因此提升,但也可能影響電池續航力。"</string>
+ <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"這些應用程式即使是在閒置狀態下,也會持續運作。應用程式的功能會因而提升,但也可能影響電池續航力。"</string>
<string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"停止"</string>
<string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"已停止"</string>
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"完成"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"已摺疊"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"已展開"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"觸控筆電量:<xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"將觸控筆接上充電器"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"觸控筆電力不足"</string>
<string name="video_camera" msgid="7654002575156149298">"攝影機"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜尋快速鍵"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收合圖示"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展開圖示"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"鍵盤背光"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 級,共 %2$d 級"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"居家控制"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 2e03bd5..a0a57ac 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -281,8 +281,10 @@
<string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Vula ngokuzenzekela futhi kusasa"</string>
<string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Izakhi ezifana nokuthi Ukwabelana Ngokushesha kanye nokuthi Thola Idivayisi Yami zisebenzisa i-Bluetooth"</string>
<string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"IBluetooth izovuleka kusasa ekuseni"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Ukwabelana Ngokuqoshiwe"</string>
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Ukwabelana Ngomsindo"</string>
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+ <skip />
+ <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+ <skip />
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ibhethri"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Umsindo"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ihedisethi"</string>
@@ -361,18 +363,13 @@
<string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Kuthinteke yiphi ingxenye yokusebenzisa idivayisi?"</string>
<string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Khetha uhlobo lwenkinga"</string>
<string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Irekhodi lesikrini"</string>
- <!-- no translation found for performance (6552785217174378320) -->
- <skip />
- <!-- no translation found for user_interface (3712869377953950887) -->
- <skip />
- <!-- no translation found for thermal (6758074791325414831) -->
- <skip />
+ <string name="performance" msgid="6552785217174378320">"Ukusebenza"</string>
+ <string name="user_interface" msgid="3712869377953950887">"Okusetshenziswa Kubonwa"</string>
+ <string name="thermal" msgid="6758074791325414831">"Ithermal"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Imodi yesandla esisodwa"</string>
<string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Izinsizakuzwa"</string>
- <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) -->
- <skip />
- <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) -->
- <skip />
+ <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Kuyasebenza"</string>
+ <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Inqamukile"</string>
<string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Izinsizakuzwa"</string>
<string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Bhangqa idivayisi entsha"</string>
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Chofoza ukuze ubhangqe idivayisi entsha"</string>
@@ -1275,8 +1272,7 @@
<string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"kugoqiwe"</string>
<string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"kuvuliwe"</string>
<string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
- <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) -->
- <skip />
+ <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Ibhethri lestylus <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Xhuma i-stylus yakho kushaja"</string>
<string name="stylus_battery_low" msgid="7134370101603167096">"Ibhethri le-stylus liphansi"</string>
<string name="video_camera" msgid="7654002575156149298">"Ikhamera yevidiyo"</string>
@@ -1328,8 +1324,7 @@
<string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Sesha izinqamuleli"</string>
<string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Goqa isithonjana"</string>
<string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Nweba isithonjana"</string>
- <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) -->
- <skip />
+ <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"noma"</string>
<string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Ilambu lekhibhodi"</string>
<string name="keyboard_backlight_value" msgid="7336398765584393538">"Ileveli %1$d ka-%2$d"</string>
<string name="home_controls_dream_label" msgid="6567105701292324257">"Izilawuli Zasekhaya"</string>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index d308c3d..e825fc5 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -912,6 +912,10 @@
obvious when corner radii differ.-->
<dimen name="communal_enforced_rounded_corner_max_radius">28dp</dimen>
+ <!-- Width and height used to filter widgets displayed in the communal widget picker -->
+ <dimen name="communal_widget_picker_desired_width">360dp</dimen>
+ <dimen name="communal_widget_picker_desired_height">240dp</dimen>
+
<!-- The width/height of the unlock icon view on keyguard. -->
<dimen name="keyguard_lock_height">42dp</dimen>
<dimen name="keyguard_lock_padding">20dp</dimen>
@@ -1118,6 +1122,8 @@
<dimen name="biometric_prompt_panel_max_width">640dp</dimen>
<dimen name="biometric_prompt_land_small_horizontal_guideline_padding">344dp</dimen>
<dimen name="biometric_prompt_two_pane_udfps_horizontal_guideline_padding">114dp</dimen>
+ <dimen name="biometric_prompt_two_pane_udfps_shorter_content_width">216dp</dimen>
+ <dimen name="biometric_prompt_two_pane_udfps_shorter_horizontal_guideline_padding">661dp</dimen>
<dimen name="biometric_prompt_two_pane_medium_horizontal_guideline_padding">640dp</dimen>
<dimen name="biometric_prompt_one_pane_medium_top_guideline_padding">119dp</dimen>
<dimen name="biometric_prompt_one_pane_medium_horizontal_guideline_padding">0dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index c2ca4da..79be2b1 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -319,6 +319,33 @@
<string name="screenrecord_save_error">Error saving screen recording</string>
<!-- A toast message shown when the screen recording cannot be started due to a generic error [CHAR LIMIT=NONE] -->
<string name="screenrecord_start_error">Error starting screen recording</string>
+ <!-- Title for a dialog shown to the user that will let them stop recording their screen [CHAR LIMIT=50] -->
+ <string name="screenrecord_stop_dialog_title">Stop recording screen?</string>
+ <!-- Text telling a user that they will stop recording their screen if they click the "Stop recording" button [CHAR LIMIT=100] -->
+ <string name="screenrecord_stop_dialog_message">You will stop recording your screen</string>
+ <!-- Button to stop a screen recording [CHAR LIMIT=35] -->
+ <string name="screenrecord_stop_dialog_button">Stop recording</string>
+
+ <!-- Title for a dialog shown to the user that will let them stop sharing their screen to another app on the device [CHAR LIMIT=50] -->
+ <string name="share_to_app_stop_dialog_title">Stop sharing screen?</string>
+ <!-- Text telling a user that they will stop sharing their screen if they click the "Stop sharing" button [CHAR LIMIT=100] -->
+ <string name="share_to_app_stop_dialog_message">You will stop sharing your screen</string>
+ <!-- Text telling a user that they will stop sharing the contents of the specified [app_name] if they click the "Stop sharing" button. Note that the app name will appear in bold. [CHAR LIMIT=100] -->
+ <string name="share_to_app_stop_dialog_message_specific_app">You will stop sharing <b><xliff:g id="app_name" example="Photos App">%1$s</xliff:g></b></string>
+ <!-- Button to stop screen sharing [CHAR LIMIT=35] -->
+ <string name="share_to_app_stop_dialog_button">Stop sharing</string>
+
+ <!-- Title for a dialog shown to the user that will let them stop casting their screen to a different device [CHAR LIMIT=50] -->
+ <string name="cast_to_other_device_stop_dialog_title">Stop casting screen?</string>
+ <!-- Text telling a user that they will stop casting their screen to a different device if they click the "Stop casting" button [CHAR LIMIT=100] -->
+ <string name="cast_to_other_device_stop_dialog_message">You will stop casting your screen</string>
+ <!-- Text telling a user that they will stop casting the contents of the specified [app_name] to a different device if they click the "Stop casting" button. Note that the app name will appear in bold. [CHAR LIMIT=100] -->
+ <string name="cast_to_other_device_stop_dialog_message_specific_app">You will stop casting <b><xliff:g id="app_name" example="Photos App">%1$s</xliff:g></b></string>
+ <!-- Button to stop screen casting to a different device [CHAR LIMIT=35] -->
+ <string name="cast_to_other_device_stop_dialog_button">Stop casting</string>
+
+ <!-- Button to close a dialog without doing any action [CHAR LIMIT=20] -->
+ <string name="close_dialog_button">Close</string>
<!-- Notification title displayed for issue recording [CHAR LIMIT=50]-->
<string name="issuerecord_title">Issue Recorder</string>
@@ -344,7 +371,7 @@
<!-- Cling help message title when hiding the navigation bar entering immersive mode [CHAR LIMIT=none] -->
<string name="immersive_cling_title">Viewing full screen</string>
<!-- Cling help message description when hiding the navigation bar entering immersive mode [CHAR LIMIT=none] -->
- <string name="immersive_cling_description">To exit, swipe down from the top.</string>
+ <string name="immersive_cling_description">To exit, swipe down from the top of your screen</string>
<!-- Cling help message confirmation button when hiding the navigation bar entering immersive mode [CHAR LIMIT=30] -->
<string name="immersive_cling_positive">Got it</string>
@@ -693,15 +720,15 @@
<!-- QuickSettings: Accessibility label to activate a device [CHAR LIMIT=NONE]-->
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate">activate</string>
<!-- QuickSettings: Bluetooth auto on tomorrow [CHAR LIMIT=NONE]-->
- <string name="turn_on_bluetooth_auto_tomorrow">Automatically turn on again tomorrow</string>
+ <string name="turn_on_bluetooth_auto_tomorrow">Automatically turn on tomorrow</string>
<!-- QuickSettings: Bluetooth auto on info text when disabled [CHAR LIMIT=NONE]-->
<string name="turn_on_bluetooth_auto_info_disabled">Features like Quick Share and Find My Device use Bluetooth</string>
<!-- QuickSettings: Bluetooth auto on info text when enabled [CHAR LIMIT=NONE]-->
<string name="turn_on_bluetooth_auto_info_enabled">Bluetooth will turn on tomorrow morning</string>
<!-- QuickSettings: Bluetooth dialog audio sharing button text [CHAR LIMIT=50]-->
- <string name="quick_settings_bluetooth_audio_sharing_button">Audio Sharing</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button">Share audio</string>
<!-- QuickSettings: Bluetooth dialog audio sharing button text when sharing audio [CHAR LIMIT=50]-->
- <string name="quick_settings_bluetooth_audio_sharing_button_sharing">Sharing Audio</string>
+ <string name="quick_settings_bluetooth_audio_sharing_button_sharing">Sharing audio</string>
<!-- QuickSettings: Bluetooth secondary label for the battery level of a connected device [CHAR LIMIT=20]-->
<string name="quick_settings_bluetooth_secondary_label_battery_level"><xliff:g id="battery_level_as_percentage">%s</xliff:g> battery</string>
@@ -3538,6 +3565,15 @@
-->
<string name="shortcut_helper_key_combinations_or_separator">or</string>
+ <!-- Label for button opening tutorial for back gesture on touchpad [CHAR LIMIT=NONE] -->
+ <string name="touchpad_tutorial_back_gesture_button">Back gesture</string>
+ <!-- Label for button opening tutorial for back gesture on touchpad [CHAR LIMIT=NONE] -->
+ <string name="touchpad_tutorial_home_gesture_button">Home gesture</string>
+ <!-- Label for button opening tutorial on using Action key from keyboard [CHAR LIMIT=NONE] -->
+ <string name="touchpad_tutorial_action_key_button">Action key</string>
+ <!-- Label for button finishing touchpad tutorial [CHAR LIMIT=NONE] -->
+ <string name="touchpad_tutorial_done_button">Done</string>
+
<!-- Content description for keyboard backlight brightness dialog [CHAR LIMIT=NONE] -->
<string name="keyboard_backlight_dialog_title">Keyboard backlight</string>
<!-- Content description for keyboard backlight brightness value [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt
index 8979ef1..12d881b 100644
--- a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt
+++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt
@@ -25,7 +25,11 @@
import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
import android.content.Context
import android.content.pm.PackageManager
+import android.graphics.Bitmap
+import android.graphics.Canvas
import android.graphics.Insets
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
import android.hardware.biometrics.BiometricManager.Authenticators
import android.hardware.biometrics.PromptInfo
import android.hardware.biometrics.SensorPropertiesInternal
@@ -122,4 +126,26 @@
return windowMetrics?.windowInsets?.getInsets(WindowInsets.Type.navigationBars())
?: Insets.NONE
}
+
+ /** Converts `drawable` to a [Bitmap]. */
+ @JvmStatic
+ fun Drawable?.toBitmap(): Bitmap? {
+ if (this == null) {
+ return null
+ }
+ if (this is BitmapDrawable) {
+ return bitmap
+ }
+ val bitmap: Bitmap =
+ if (intrinsicWidth <= 0 || intrinsicHeight <= 0) {
+ Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
+ // Single color bitmap will be created of 1x1 pixel
+ } else {
+ Bitmap.createBitmap(intrinsicWidth, intrinsicHeight, Bitmap.Config.ARGB_8888)
+ }
+ val canvas = Canvas(bitmap)
+ setBounds(0, 0, canvas.width, canvas.height)
+ draw(canvas)
+ return bitmap
+ }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt
index 7a8c82c..4fd5456 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt
@@ -37,10 +37,17 @@
private lateinit var rootView: ViewGroup
private var translationMax = 0f
+ /**
+ * Initializes the animator, it is allowed to call this method multiple times, for example
+ * to update the rootView or maximum translation
+ */
fun init(rootView: ViewGroup, translationMax: Float) {
+ if (!::rootView.isInitialized) {
+ progressProvider.addCallback(this)
+ }
+
this.rootView = rootView
this.translationMax = translationMax
- progressProvider.addCallback(this)
}
override fun onTransitionStarted() {
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 86c807b..5dcf161 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -178,6 +178,7 @@
smallClockOnAttachStateChangeListener =
object : OnAttachStateChangeListener {
var pastVisibility: Int? = null
+
override fun onViewAttachedToWindow(view: View) {
clock.events.onTimeFormatChanged(DateFormat.is24HourFormat(context))
// Match the asing for view.parent's layout classes.
@@ -213,6 +214,7 @@
override fun onViewAttachedToWindow(p0: View) {
clock.events.onTimeFormatChanged(DateFormat.is24HourFormat(context))
}
+
override fun onViewDetachedFromWindow(p0: View) {}
}
clock.largeClock.view.addOnAttachStateChangeListener(largeClockOnAttachStateChangeListener)
@@ -284,8 +286,10 @@
var smallRegionSampler: RegionSampler? = null
private set
+
var largeRegionSampler: RegionSampler? = null
private set
+
var smallTimeListener: TimeListener? = null
var largeTimeListener: TimeListener? = null
val shouldTimeListenerRun: Boolean
@@ -560,7 +564,7 @@
internal fun listenForAnyStateToAodTransition(scope: CoroutineScope): Job {
return scope.launch {
keyguardTransitionInteractor
- .transitionStepsToState(AOD)
+ .transition(Edge.create(to = AOD))
.filter { it.transitionState == TransitionState.STARTED }
.filter { it.from != LOCKSCREEN }
.collect { handleDoze(1f) }
@@ -571,7 +575,7 @@
internal fun listenForAnyStateToLockscreenTransition(scope: CoroutineScope): Job {
return scope.launch {
keyguardTransitionInteractor
- .transitionStepsToState(LOCKSCREEN)
+ .transition(Edge.create(to = LOCKSCREEN))
.filter { it.transitionState == TransitionState.STARTED }
.filter { it.from != AOD }
.collect { handleDoze(0f) }
@@ -586,7 +590,7 @@
internal fun listenForAnyStateToDozingTransition(scope: CoroutineScope): Job {
return scope.launch {
keyguardTransitionInteractor
- .transitionStepsToState(DOZING)
+ .transition(Edge.create(to = DOZING))
.filter { it.transitionState == TransitionState.FINISHED }
.collect { handleDoze(1f) }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
index 7170be61..19d918f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
@@ -17,16 +17,21 @@
package com.android.keyguard
import android.content.Context
-import android.view.ViewGroup
-import com.android.systemui.res.R
+import android.view.View
+import com.android.systemui.keyguard.MigrateClocksToBlueprint
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.statusbar.StatusBarState.KEYGUARD
+import com.android.systemui.res.R
+import com.android.systemui.shared.R as sharedR
+import com.android.systemui.shade.NotificationShadeWindowView
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.END
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.START
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.ViewIdToTranslate
+import com.android.systemui.statusbar.StatusBarState.KEYGUARD
import com.android.systemui.unfold.SysUIUnfoldScope
-import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import com.android.systemui.unfold.dagger.NaturalRotation
import javax.inject.Inject
/**
@@ -38,8 +43,10 @@
@Inject
constructor(
private val context: Context,
+ private val keyguardRootView: KeyguardRootView,
+ private val shadeWindowView: NotificationShadeWindowView,
statusBarStateController: StatusBarStateController,
- unfoldProgressProvider: NaturalRotationUnfoldProgressProvider,
+ @NaturalRotation unfoldProgressProvider: UnfoldTransitionProgressProvider,
) {
/** Certain views only need to move if they are not currently centered */
@@ -50,27 +57,94 @@
private val filterKeyguard: () -> Boolean = { statusBarStateController.getState() == KEYGUARD }
private val translateAnimator by lazy {
+ val smartSpaceViews = if (MigrateClocksToBlueprint.isEnabled) {
+ // Use scrollX instead of translationX as translation is already set by [AodBurnInLayer]
+ val scrollXTranslation = { view: View, translation: Float ->
+ view.scrollX = -translation.toInt()
+ }
+
+ setOf(
+ ViewIdToTranslate(
+ viewId = sharedR.id.date_smartspace_view,
+ direction = START,
+ shouldBeAnimated = filterKeyguard,
+ translateFunc = scrollXTranslation,
+ ),
+ ViewIdToTranslate(
+ viewId = sharedR.id.bc_smartspace_view,
+ direction = START,
+ shouldBeAnimated = filterKeyguard,
+ translateFunc = scrollXTranslation,
+ ),
+ ViewIdToTranslate(
+ viewId = sharedR.id.weather_smartspace_view,
+ direction = START,
+ shouldBeAnimated = filterKeyguard,
+ translateFunc = scrollXTranslation,
+ )
+ )
+ } else {
+ setOf(ViewIdToTranslate(
+ viewId = R.id.keyguard_status_area,
+ direction = START,
+ shouldBeAnimated = filterKeyguard,
+ translateFunc = { view, value ->
+ (view as? KeyguardStatusAreaView)?.translateXFromUnfold = value
+ }
+ ))
+ }
+
UnfoldConstantTranslateAnimator(
viewsIdToTranslate =
setOf(
- ViewIdToTranslate(R.id.keyguard_status_area, START, filterKeyguard,
- { view, value ->
- (view as? KeyguardStatusAreaView)?.translateXFromUnfold = value
- }),
ViewIdToTranslate(
- R.id.lockscreen_clock_view_large, START, filterKeyguardAndSplitShadeOnly),
- ViewIdToTranslate(R.id.lockscreen_clock_view, START, filterKeyguard),
+ viewId = R.id.lockscreen_clock_view_large,
+ direction = START,
+ shouldBeAnimated = filterKeyguardAndSplitShadeOnly
+ ),
ViewIdToTranslate(
- R.id.notification_stack_scroller, END, filterKeyguardAndSplitShadeOnly),
- ViewIdToTranslate(R.id.start_button, START, filterKeyguard),
- ViewIdToTranslate(R.id.end_button, END, filterKeyguard)),
- progressProvider = unfoldProgressProvider)
+ viewId = R.id.lockscreen_clock_view,
+ direction = START,
+ shouldBeAnimated = filterKeyguard
+ ),
+ ViewIdToTranslate(
+ viewId = R.id.notification_stack_scroller,
+ direction = END,
+ shouldBeAnimated = filterKeyguardAndSplitShadeOnly
+ )
+ ) + smartSpaceViews,
+ progressProvider = unfoldProgressProvider
+ )
}
- /** Relies on the [parent] to locate views to translate. */
- fun setup(parent: ViewGroup) {
+ private val shortcutButtonsAnimator by lazy {
+ UnfoldConstantTranslateAnimator(
+ viewsIdToTranslate =
+ setOf(
+ ViewIdToTranslate(
+ viewId = R.id.start_button,
+ direction = START,
+ shouldBeAnimated = filterKeyguard
+ ),
+ ViewIdToTranslate(
+ viewId = R.id.end_button,
+ direction = END,
+ shouldBeAnimated = filterKeyguard
+ )
+ ),
+ progressProvider = unfoldProgressProvider
+ )
+ }
+
+ /** Initializes the keyguard fold/unfold transition */
+ fun setup() {
val translationMax =
context.resources.getDimensionPixelSize(R.dimen.keyguard_unfold_translation_x).toFloat()
- translateAnimator.init(parent, translationMax)
+
+ translateAnimator.init(shadeWindowView, translationMax)
+
+ // Use keyguard root view as there is another instance of start/end buttons with the same ID
+ // outside of the keyguard root view
+ shortcutButtonsAnimator.init(keyguardRootView, translationMax)
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java
index a9fd340..03b13fe 100644
--- a/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java
@@ -70,7 +70,7 @@
import com.android.systemui.keyguard.MigrateClocksToBlueprint;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
-import com.android.systemui.keyguard.shared.model.TransitionStep;
+import com.android.systemui.keyguard.shared.model.KeyguardState;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.res.R;
@@ -167,9 +167,9 @@
private LockIconView mView;
@VisibleForTesting
- final Consumer<TransitionStep> mDozeTransitionCallback = (TransitionStep step) -> {
- mInterpolatedDarkAmount = step.getValue();
- mView.setDozeAmount(step.getValue());
+ final Consumer<Float> mDozeTransitionCallback = (Float value) -> {
+ mInterpolatedDarkAmount = value;
+ mView.setDozeAmount(value);
updateBurnInOffsets();
};
@@ -265,7 +265,7 @@
mView.setAccessibilityDelegate(mAccessibilityDelegate);
if (mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) {
- collectFlow(mView, mTransitionInteractor.getDozeAmountTransition(),
+ collectFlow(mView, mTransitionInteractor.transitionValue(KeyguardState.AOD),
mDozeTransitionCallback);
collectFlow(mView, mKeyguardInteractor.isDozing(), mIsDozingCallback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
index 0bd6d6e..3c4c003 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
@@ -30,7 +30,12 @@
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Region;
+import android.os.Handler;
+import android.util.Log;
import android.view.AttachedSurfaceControl;
+import android.view.Display;
+import android.view.IRotationWatcher;
+import android.view.IWindowManager;
import android.view.LayoutInflater;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
@@ -46,15 +51,18 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.res.R;
+import com.android.systemui.util.leak.RotationUtils;
import java.util.concurrent.Executor;
import java.util.function.Supplier;
class FullscreenMagnificationController implements ComponentCallbacks {
+ private static final String TAG = "FullscreenMagnificationController";
private final Context mContext;
private final AccessibilityManager mAccessibilityManager;
private final WindowManager mWindowManager;
+ private final IWindowManager mIWindowManager;
private Supplier<SurfaceControlViewHost> mScvhSupplier;
private SurfaceControlViewHost mSurfaceControlViewHost = null;
private SurfaceControl mBorderSurfaceControl = null;
@@ -65,33 +73,50 @@
private final int mDisplayId;
private static final Region sEmptyRegion = new Region();
private ValueAnimator mShowHideBorderAnimator;
+ private Handler mHandler;
private Executor mExecutor;
private boolean mFullscreenMagnificationActivated = false;
private final Configuration mConfiguration;
+ private final Runnable mShowBorderRunnable = this::showBorderWithNullCheck;
+ private int mRotation;
+ private final IRotationWatcher mRotationWatcher = new IRotationWatcher.Stub() {
+ @Override
+ public void onRotationChanged(final int rotation) {
+ handleScreenRotation();
+ }
+ };
+ private final long mLongAnimationTimeMs;
FullscreenMagnificationController(
@UiContext Context context,
- Executor executor,
+ @Main Handler handler,
+ @Main Executor executor,
AccessibilityManager accessibilityManager,
WindowManager windowManager,
+ IWindowManager iWindowManager,
Supplier<SurfaceControlViewHost> scvhSupplier) {
- this(context, executor, accessibilityManager, windowManager, scvhSupplier,
- new SurfaceControl.Transaction(), createNullTargetObjectAnimator(context));
+ this(context, handler, executor, accessibilityManager,
+ windowManager, iWindowManager, scvhSupplier,
+ new SurfaceControl.Transaction(), null);
}
@VisibleForTesting
FullscreenMagnificationController(
@UiContext Context context,
+ @Main Handler handler,
@Main Executor executor,
AccessibilityManager accessibilityManager,
WindowManager windowManager,
+ IWindowManager iWindowManager,
Supplier<SurfaceControlViewHost> scvhSupplier,
SurfaceControl.Transaction transaction,
ValueAnimator valueAnimator) {
mContext = context;
+ mHandler = handler;
mExecutor = executor;
mAccessibilityManager = accessibilityManager;
mWindowManager = windowManager;
+ mIWindowManager = iWindowManager;
mWindowBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
mTransaction = transaction;
mScvhSupplier = scvhSupplier;
@@ -101,7 +126,10 @@
R.dimen.magnifier_border_width_fullscreen);
mDisplayId = mContext.getDisplayId();
mConfiguration = new Configuration(context.getResources().getConfiguration());
- mShowHideBorderAnimator = valueAnimator;
+ mLongAnimationTimeMs = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_longAnimTime);
+ mShowHideBorderAnimator = (valueAnimator == null)
+ ? createNullTargetObjectAnimator() : valueAnimator;
mShowHideBorderAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(@NonNull Animator animation, boolean isReverse) {
@@ -114,15 +142,13 @@
});
}
- private static ValueAnimator createNullTargetObjectAnimator(Context context) {
+ private ValueAnimator createNullTargetObjectAnimator() {
final ValueAnimator valueAnimator =
ObjectAnimator.ofFloat(/* target= */ null, View.ALPHA, 0f, 1f);
Interpolator interpolator = new AccelerateDecelerateInterpolator();
- final long longAnimationDuration = context.getResources().getInteger(
- com.android.internal.R.integer.config_longAnimTime);
valueAnimator.setInterpolator(interpolator);
- valueAnimator.setDuration(longAnimationDuration);
+ valueAnimator.setDuration(mLongAnimationTimeMs);
return valueAnimator;
}
@@ -149,7 +175,11 @@
*/
@UiThread
private void removeFullscreenMagnificationBorder() {
+ if (mHandler.hasCallbacks(mShowBorderRunnable)) {
+ mHandler.removeCallbacks(mShowBorderRunnable);
+ }
mContext.unregisterComponentCallbacks(this);
+
mShowHideBorderAnimator.reverse();
}
@@ -161,6 +191,11 @@
if (mFullscreenBorder != null) {
mFullscreenBorder = null;
+ try {
+ mIWindowManager.removeRotationWatcher(mRotationWatcher);
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to remove rotation watcher", e);
+ }
}
}
@@ -186,6 +221,11 @@
mSurfaceControlViewHost = mScvhSupplier.get();
mSurfaceControlViewHost.setView(mFullscreenBorder, getBorderLayoutParams());
mBorderSurfaceControl = mSurfaceControlViewHost.getSurfacePackage().getSurfaceControl();
+ try {
+ mIWindowManager.watchRotation(mRotationWatcher, Display.DEFAULT_DISPLAY);
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to register rotation watcher", e);
+ }
}
mTransaction
@@ -256,11 +296,55 @@
reCreateWindow = true;
}
- if (mFullscreenBorder != null && reCreateWindow) {
+ if (mFullscreenBorder == null) {
+ return;
+ }
+
+ if (reCreateWindow) {
final int newWidth = mWindowBounds.width() + 2 * mBorderOffset;
final int newHeight = mWindowBounds.height() + 2 * mBorderOffset;
mSurfaceControlViewHost.relayout(newWidth, newHeight);
}
+
+ // Rotating from Landscape to ReverseLandscape will not trigger the config changes in
+ // CONFIG_SCREEN_SIZE and CONFIG_ORIENTATION. Therefore, we would like to check the device
+ // rotation separately.
+ // Since there's a possibility that {@link onConfigurationChanged} comes before
+ // {@link onRotationChanged}, we would like to handle screen rotation in either case that
+ // happens earlier.
+ int newRotation = RotationUtils.getRotation(mContext);
+ if (newRotation != mRotation) {
+ mRotation = newRotation;
+ handleScreenRotation();
+ }
+ }
+
+ private boolean isActivated() {
+ return mFullscreenBorder != null;
+ }
+
+ private void handleScreenRotation() {
+ if (!isActivated()) {
+ return;
+ }
+
+ if (mHandler.hasCallbacks(mShowBorderRunnable)) {
+ mHandler.removeCallbacks(mShowBorderRunnable);
+ }
+
+ // We hide the border immediately as early as possible to beat the redrawing of window
+ // in response to the orientation change so users won't see a weird shape border.
+ mHandler.postAtFrontOfQueue(() -> {
+ mFullscreenBorder.setAlpha(0f);
+ });
+
+ mHandler.postDelayed(mShowBorderRunnable, mLongAnimationTimeMs);
+ }
+
+ private void showBorderWithNullCheck() {
+ if (mShowHideBorderAnimator != null) {
+ mShowHideBorderAnimator.start();
+ }
}
private void updateDimensions() {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
index 35c2024..e22a4e4 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
@@ -34,6 +34,7 @@
import android.os.Message;
import android.util.SparseArray;
import android.view.Display;
+import android.view.IWindowManager;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.WindowManager;
@@ -148,13 +149,19 @@
DisplayIdIndexSupplier<FullscreenMagnificationController> {
private final Context mContext;
+ private final Handler mHandler;
private final Executor mExecutor;
+ private final IWindowManager mIWindowManager;
- FullscreenMagnificationControllerSupplier(Context context, DisplayManager displayManager,
- Executor executor) {
+ FullscreenMagnificationControllerSupplier(Context context,
+ DisplayManager displayManager,
+ Handler handler,
+ Executor executor, IWindowManager iWindowManager) {
super(displayManager);
mContext = context;
+ mHandler = handler;
mExecutor = executor;
+ mIWindowManager = iWindowManager;
}
@Override
@@ -166,9 +173,11 @@
windowContext.setTheme(com.android.systemui.res.R.style.Theme_SystemUI);
return new FullscreenMagnificationController(
windowContext,
+ mHandler,
mExecutor,
windowContext.getSystemService(AccessibilityManager.class),
windowContext.getSystemService(WindowManager.class),
+ mIWindowManager,
scvhSupplier);
}
}
@@ -211,14 +220,16 @@
DisplayIdIndexSupplier<MagnificationSettingsController> mMagnificationSettingsSupplier;
@Inject
- public Magnification(Context context, @Main Handler mainHandler, @Main Executor executor,
+ public Magnification(Context context,
+ @Main Handler mainHandler, @Main Executor executor,
CommandQueue commandQueue, ModeSwitchesController modeSwitchesController,
SysUiState sysUiState, OverviewProxyService overviewProxyService,
SecureSettings secureSettings, DisplayTracker displayTracker,
- DisplayManager displayManager, AccessibilityLogger a11yLogger) {
+ DisplayManager displayManager, AccessibilityLogger a11yLogger,
+ IWindowManager iWindowManager) {
this(context, mainHandler.getLooper(), executor, commandQueue,
modeSwitchesController, sysUiState, overviewProxyService, secureSettings,
- displayTracker, displayManager, a11yLogger);
+ displayTracker, displayManager, a11yLogger, iWindowManager);
}
@VisibleForTesting
@@ -226,7 +237,8 @@
CommandQueue commandQueue, ModeSwitchesController modeSwitchesController,
SysUiState sysUiState, OverviewProxyService overviewProxyService,
SecureSettings secureSettings, DisplayTracker displayTracker,
- DisplayManager displayManager, AccessibilityLogger a11yLogger) {
+ DisplayManager displayManager, AccessibilityLogger a11yLogger,
+ IWindowManager iWindowManager) {
mContext = context;
mHandler = new Handler(looper) {
@Override
@@ -248,7 +260,7 @@
mHandler, mWindowMagnifierCallback,
displayManager, sysUiState, secureSettings);
mFullscreenMagnificationControllerSupplier = new FullscreenMagnificationControllerSupplier(
- context, displayManager, mExecutor);
+ context, displayManager, mHandler, mExecutor, iWindowManager);
mMagnificationSettingsSupplier = new SettingsSupplier(context,
mMagnificationSettingsControllerCallback, displayManager, secureSettings);
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/dagger/AmbientModule.kt b/packages/SystemUI/src/com/android/systemui/ambient/dagger/AmbientModule.kt
index ea00398..b0314d8 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/dagger/AmbientModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/ambient/dagger/AmbientModule.kt
@@ -16,11 +16,19 @@
package com.android.systemui.ambient.dagger
+import com.android.systemui.ambient.statusbar.dagger.AmbientStatusBarComponent
import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent
import com.android.systemui.ambient.touch.dagger.InputSessionComponent
import dagger.Module
-@Module(subcomponents = [AmbientTouchComponent::class, InputSessionComponent::class])
+@Module(
+ subcomponents =
+ [
+ AmbientStatusBarComponent::class,
+ AmbientTouchComponent::class,
+ InputSessionComponent::class,
+ ]
+)
interface AmbientModule {
companion object {
const val TOUCH_HANDLERS = "touch_handlers"
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/statusbar/dagger/AmbientStatusBarComponent.kt b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/dagger/AmbientStatusBarComponent.kt
new file mode 100644
index 0000000..8ad4d00
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/dagger/AmbientStatusBarComponent.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.ambient.statusbar.dagger
+
+import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarView
+import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController
+import dagger.BindsInstance
+import dagger.Subcomponent
+
+/**
+ * [AmbientStatusBarComponent] can be used for displaying a status bar over ambient surfaces like
+ * the dream or communal hub.
+ */
+@Subcomponent
+interface AmbientStatusBarComponent {
+ @Subcomponent.Factory
+ interface Factory {
+ fun create(
+ @BindsInstance view: AmbientStatusBarView,
+ ): AmbientStatusBarComponent
+ }
+
+ /** Builds a [AmbientStatusBarViewController] */
+ fun getController(): AmbientStatusBarViewController
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarView.java
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
rename to packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarView.java
index 8e77079..aa96231 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarView.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.dreams;
+package com.android.systemui.ambient.statusbar.ui;
import android.annotation.IntDef;
import android.annotation.Nullable;
@@ -39,10 +39,10 @@
import java.util.Objects;
/**
- * {@link DreamOverlayStatusBarView} is the view responsible for displaying the status bar in a
+ * {@link AmbientStatusBarView} is the view responsible for displaying the status bar in a
* dream. The status bar displays conditional status icons such as "priority mode" and "no wifi".
*/
-public class DreamOverlayStatusBarView extends ConstraintLayout {
+public class AmbientStatusBarView extends ConstraintLayout {
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = { "STATUS_ICON_" }, value = {
@@ -76,20 +76,20 @@
private static final float KEY_SHADOW_ALPHA = 0.35f;
private static final float AMBIENT_SHADOW_ALPHA = 0.4f;
- public DreamOverlayStatusBarView(Context context) {
+ public AmbientStatusBarView(Context context) {
this(context, null);
}
- public DreamOverlayStatusBarView(Context context, AttributeSet attrs) {
+ public AmbientStatusBarView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
- public DreamOverlayStatusBarView(Context context, AttributeSet attrs, int defStyleAttr) {
+ public AmbientStatusBarView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
mContext = context;
}
- public DreamOverlayStatusBarView(
+ public AmbientStatusBarView(
Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewController.java
similarity index 83%
rename from packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
rename to packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewController.java
index da72a56..abdc333 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewController.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.dreams;
+package com.android.systemui.ambient.statusbar.ui;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
@@ -30,21 +30,24 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dreams.DreamLogger;
+import com.android.systemui.dreams.DreamOverlayNotificationCountProvider;
+import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.dreams.DreamOverlayStatusBarItemsProvider;
import com.android.systemui.dreams.DreamOverlayStatusBarItemsProvider.StatusBarItem;
-import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.dagger.DreamLog;
import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CrossFadeHelper;
-import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository;
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor;
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel;
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
-import com.android.systemui.touch.TouchInsetManager;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.time.DateFormatUtil;
@@ -59,13 +62,11 @@
import javax.inject.Inject;
/**
- * View controller for {@link DreamOverlayStatusBarView}.
+ * View controller for {@link AmbientStatusBarView}.
*/
-@DreamOverlayComponent.DreamOverlayScope
-public class DreamOverlayStatusBarViewController extends ViewController<DreamOverlayStatusBarView> {
+public class AmbientStatusBarViewController extends ViewController<AmbientStatusBarView> {
private static final String TAG = "DreamStatusBarCtrl";
- private final TouchInsetManager.TouchInsetSession mTouchInsetSession;
private final NextAlarmController mNextAlarmController;
private final AlarmManager mAlarmManager;
private final Resources mResources;
@@ -76,15 +77,17 @@
private final ZenModeController mZenModeController;
private final DreamOverlayStateController mDreamOverlayStateController;
private final UserTracker mUserTracker;
- private final WifiRepository mWifiRepository;
+ private final WifiInteractor mWifiInteractor;
private final StatusBarWindowStateController mStatusBarWindowStateController;
private final DreamOverlayStatusBarItemsProvider mStatusBarItemsProvider;
private final Executor mMainExecutor;
private final List<DreamOverlayStatusBarItemsProvider.StatusBarItem> mExtraStatusBarItems =
new ArrayList<>();
+ private final CommunalSceneInteractor mCommunalSceneInteractor;
private final DreamLogger mLogger;
private boolean mIsAttached;
+ private boolean mCommunalVisible;
// Whether dream entry animations are finished.
private boolean mEntryAnimationsFinished = false;
@@ -115,7 +118,7 @@
private final DreamOverlayNotificationCountProvider.Callback mNotificationCountCallback =
notificationCount -> showIcon(
- DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS,
+ AmbientStatusBarView.STATUS_ICON_NOTIFICATIONS,
notificationCount > 0,
notificationCount > 0
? buildNotificationsContentDescription(notificationCount)
@@ -125,11 +128,10 @@
this::onStatusBarItemsChanged;
@Inject
- public DreamOverlayStatusBarViewController(
- DreamOverlayStatusBarView view,
+ public AmbientStatusBarViewController(
+ AmbientStatusBarView view,
@Main Resources resources,
@Main Executor mainExecutor,
- TouchInsetManager.TouchInsetSession touchInsetSession,
AlarmManager alarmManager,
NextAlarmController nextAlarmController,
DateFormatUtil dateFormatUtil,
@@ -140,12 +142,12 @@
DreamOverlayStatusBarItemsProvider statusBarItemsProvider,
DreamOverlayStateController dreamOverlayStateController,
UserTracker userTracker,
- WifiRepository wifiRepository,
+ WifiInteractor wifiInteractor,
+ CommunalSceneInteractor communalSceneInteractor,
@DreamLog LogBuffer logBuffer) {
super(view);
mResources = resources;
mMainExecutor = mainExecutor;
- mTouchInsetSession = touchInsetSession;
mAlarmManager = alarmManager;
mNextAlarmController = nextAlarmController;
mDateFormatUtil = dateFormatUtil;
@@ -156,7 +158,8 @@
mZenModeController = zenModeController;
mDreamOverlayStateController = dreamOverlayStateController;
mUserTracker = userTracker;
- mWifiRepository = wifiRepository;
+ mWifiInteractor = wifiInteractor;
+ mCommunalSceneInteractor = communalSceneInteractor;
mLogger = new DreamLogger(logBuffer, TAG);
// Register to receive show/hide updates for the system status bar. Our custom status bar
@@ -170,10 +173,16 @@
collectFlow(
mView,
- mWifiRepository.getWifiNetwork(),
+ mWifiInteractor.getWifiNetwork(),
network -> updateWifiUnavailableStatusIcon(
network instanceof WifiNetworkModel.Active));
+ collectFlow(
+ mView,
+ mCommunalSceneInteractor.isCommunalVisible(),
+ this::onCommunalVisibleChanged
+ );
+
mNextAlarmController.addCallback(mNextAlarmCallback);
updateAlarmStatusIcon();
@@ -202,7 +211,6 @@
mView.removeAllExtraStatusBarItemViews();
mDreamOverlayStateController.setDreamOverlayStatusBarVisible(false);
mDreamOverlayStateController.removeCallback(mDreamOverlayStateCallback);
- mTouchInsetSession.clear();
mIsAttached = false;
}
@@ -212,7 +220,7 @@
*
* No-op if the dream overlay status bar should not be shown.
*/
- protected void setFadeAmount(float fadeAmount, boolean fadingOut) {
+ public void setFadeAmount(float fadeAmount, boolean fadingOut) {
updateVisibility();
if (mView.getVisibility() != View.VISIBLE) {
@@ -233,14 +241,20 @@
mView.setTranslationY(translationY);
}
+ private void onCommunalVisibleChanged(boolean visible) {
+ mCommunalVisible = visible;
+ updateVisibility();
+ }
+
private boolean shouldShowStatusBar() {
- return !mDreamOverlayStateController.isLowLightActive()
- && !mStatusBarWindowStateController.windowIsShowing();
+ return (!mDreamOverlayStateController.isLowLightActive()
+ && !mStatusBarWindowStateController.windowIsShowing())
+ || mCommunalVisible;
}
@VisibleForTesting
void updateWifiUnavailableStatusIcon(boolean available) {
- showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, !available,
+ showIcon(AmbientStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, !available,
R.string.wifi_unavailable_dream_overlay_content_description);
}
@@ -249,13 +263,13 @@
mAlarmManager.getNextAlarmClock(mUserTracker.getUserId());
final boolean hasAlarm = alarm != null && alarm.getTriggerTime() > 0;
showIcon(
- DreamOverlayStatusBarView.STATUS_ICON_ALARM_SET,
+ AmbientStatusBarView.STATUS_ICON_ALARM_SET,
hasAlarm,
hasAlarm ? buildAlarmContentDescription(alarm) : null);
}
private void updateAssistantAttentionIcon() {
- showIcon(DreamOverlayStatusBarView.STATUS_ICON_ASSISTANT_ATTENTION_ACTIVE,
+ showIcon(AmbientStatusBarView.STATUS_ICON_ASSISTANT_ATTENTION_ACTIVE,
mDreamOverlayStateController.hasAssistantAttention(),
R.string.assistant_attention_content_description);
}
@@ -284,17 +298,17 @@
.isSensorBlocked(SensorPrivacyManager.Sensors.MICROPHONE);
final boolean cameraBlocked = mSensorPrivacyController
.isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA);
- @DreamOverlayStatusBarView.StatusIconType int iconType = Resources.ID_NULL;
+ @AmbientStatusBarView.StatusIconType int iconType = Resources.ID_NULL;
showIcon(
- DreamOverlayStatusBarView.STATUS_ICON_CAMERA_DISABLED,
+ AmbientStatusBarView.STATUS_ICON_CAMERA_DISABLED,
!micBlocked && cameraBlocked,
R.string.camera_blocked_dream_overlay_content_description);
showIcon(
- DreamOverlayStatusBarView.STATUS_ICON_MIC_DISABLED,
+ AmbientStatusBarView.STATUS_ICON_MIC_DISABLED,
micBlocked && !cameraBlocked,
R.string.microphone_blocked_dream_overlay_content_description);
showIcon(
- DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED,
+ AmbientStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED,
micBlocked && cameraBlocked,
R.string.camera_and_microphone_blocked_dream_overlay_content_description);
}
@@ -308,24 +322,24 @@
private void updatePriorityModeStatusIcon() {
showIcon(
- DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON,
+ AmbientStatusBarView.STATUS_ICON_PRIORITY_MODE_ON,
mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF,
R.string.priority_mode_dream_overlay_content_description);
}
- private void showIcon(@DreamOverlayStatusBarView.StatusIconType int iconType, boolean show,
+ private void showIcon(@AmbientStatusBarView.StatusIconType int iconType, boolean show,
int contentDescriptionResId) {
showIcon(iconType, show, mResources.getString(contentDescriptionResId));
}
private void showIcon(
- @DreamOverlayStatusBarView.StatusIconType int iconType,
+ @AmbientStatusBarView.StatusIconType int iconType,
boolean show,
@Nullable String contentDescription) {
mMainExecutor.execute(() -> {
if (mIsAttached) {
mLogger.logShowOrHideStatusBarItem(
- show, DreamOverlayStatusBarView.getLoggableStatusIconType(iconType));
+ show, AmbientStatusBarView.getLoggableStatusIconType(iconType));
mView.showIcon(iconType, show, contentDescription);
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
index 298b87d..c3d9240 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
@@ -39,7 +39,6 @@
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
-import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
@@ -59,7 +58,6 @@
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
-import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
/** Class that coordinates non-HBM animations during keyguard authentication. */
@@ -307,27 +305,12 @@
@VisibleForTesting
suspend fun listenForLockscreenAodTransitions(scope: CoroutineScope): Job {
return scope.launch {
- transitionInteractor.dozeAmountTransition.collect { transitionStep ->
- if (
- transitionStep.from == AOD &&
- transitionStep.transitionState == TransitionState.CANCELED
- ) {
- if (transitionInteractor.startedKeyguardTransitionStep.first().to != AOD) {
- // If the next started transition isn't transitioning back to AOD, force
- // doze amount to be 0f (as if the transition to the lockscreen completed).
- view.onDozeAmountChanged(
- 0f,
- 0f,
- UdfpsKeyguardViewLegacy.ANIMATION_NONE,
- )
- }
- } else {
- view.onDozeAmountChanged(
- transitionStep.value,
- transitionStep.value,
- UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN,
- )
- }
+ transitionInteractor.transitionValue(AOD).collect {
+ view.onDozeAmountChanged(
+ it,
+ it,
+ UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN,
+ )
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
index 9b14d6f..2fa4a89 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
@@ -139,9 +139,10 @@
}
.stateIn(
backgroundScope,
- started = SharingStarted.WhileSubscribed(),
+ started = SharingStarted.Eagerly,
initialValue = false,
)
+
private fun dpiFromPx(size: Float, densityDpi: Int): Float {
val densityRatio = densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT
return size / densityRatio
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
index 4f96c1e..348b423 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
@@ -40,8 +40,7 @@
operationInfo = operationInfo,
showEmergencyCallButton = info.isShowEmergencyCallButton
) {
- val logoRes: Int = info.logoRes
- val logoBitmap: Bitmap? = info.logoBitmap
+ val logoBitmap: Bitmap? = info.logo
val logoDescription: String? = info.logoDescription
val negativeButtonText: String = info.negativeButtonText?.toString() ?: ""
val componentNameForConfirmDeviceCredentialActivity: ComponentName? =
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
index 628b533..a87ee24 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
@@ -51,6 +51,7 @@
import com.android.systemui.biometrics.ui.viewmodel.isMedium
import com.android.systemui.biometrics.ui.viewmodel.isNullOrNotSmall
import com.android.systemui.biometrics.ui.viewmodel.isSmall
+import com.android.systemui.biometrics.ui.viewmodel.isTop
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
import kotlin.math.abs
@@ -100,13 +101,13 @@
val iconHolderView = view.requireViewById<View>(R.id.biometric_icon)
val panelView = view.requireViewById<View>(R.id.panel)
val cornerRadius = view.resources.getDimension(R.dimen.biometric_dialog_corner_size)
- val cornerRadiusPx =
+ val pxToDp =
TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_DIP,
- cornerRadius,
- view.resources.displayMetrics
- )
- .toInt()
+ TypedValue.COMPLEX_UNIT_DIP,
+ 1f,
+ view.resources.displayMetrics
+ )
+ val cornerRadiusPx = (pxToDp * cornerRadius).toInt()
var currentSize: PromptSize? = null
var currentPosition: PromptPosition = PromptPosition.Bottom
@@ -132,18 +133,10 @@
cornerRadiusPx.toFloat()
)
}
+ PromptPosition.Bottom,
PromptPosition.Top -> {
outline.setRoundRect(
0,
- -cornerRadiusPx,
- view.width,
- view.height,
- cornerRadiusPx.toFloat()
- )
- }
- PromptPosition.Bottom -> {
- outline.setRoundRect(
- 0,
0,
view.width,
view.height + cornerRadiusPx,
@@ -308,6 +301,7 @@
}
}
}
+
lifecycleScope.launch {
viewModel.iconSize.collect { iconSize ->
iconHolderView.layoutParams.width = iconSize.first
@@ -370,7 +364,7 @@
if (midGuideline != null) {
val left =
if (bounds.left >= 0) {
- bounds.left
+ abs(bounds.left)
} else {
view.width - abs(bounds.left)
}
@@ -378,13 +372,14 @@
if (bounds.right >= 0) {
view.width - abs(bounds.right)
} else {
- bounds.right
+ abs(bounds.right)
}
val mid = (left + right) / 2
mediumConstraintSet.setGuidelineBegin(midGuideline.id, mid)
}
}
}
+
lifecycleScope.launch {
combine(viewModel.hideSensorIcon, viewModel.size, ::Pair).collect {
(hideSensorIcon, size) ->
@@ -415,6 +410,33 @@
R.id.rightGuideline,
ConstraintSet.RIGHT
)
+ } else if (position.isTop) {
+ // Top position is only used for 180 rotation Udfps
+ // Requires repositioning due to sensor location at top of screen
+ mediumConstraintSet.connect(
+ R.id.scrollView,
+ ConstraintSet.TOP,
+ R.id.indicator,
+ ConstraintSet.BOTTOM
+ )
+ mediumConstraintSet.connect(
+ R.id.scrollView,
+ ConstraintSet.BOTTOM,
+ R.id.button_bar,
+ ConstraintSet.TOP
+ )
+ mediumConstraintSet.connect(
+ R.id.panel,
+ ConstraintSet.TOP,
+ R.id.biometric_icon,
+ ConstraintSet.TOP
+ )
+ mediumConstraintSet.setMargin(
+ R.id.panel,
+ ConstraintSet.TOP,
+ (-24 * pxToDp).toInt()
+ )
+ mediumConstraintSet.setVerticalBias(R.id.scrollView, 0f)
}
when {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index a39a74f..7d494a5 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -30,6 +30,7 @@
import android.hardware.biometrics.Flags.customBiometricPrompt
import android.hardware.biometrics.PromptContentView
import android.os.UserHandle
+import android.text.TextPaint
import android.util.Log
import android.util.RotationUtils
import android.view.HapticFeedbackConstants
@@ -52,6 +53,7 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus
import com.android.systemui.res.R
+import com.android.systemui.util.kotlin.combine
import javax.inject.Inject
import kotlinx.coroutines.Job
import kotlinx.coroutines.coroutineScope
@@ -260,17 +262,19 @@
val position: Flow<PromptPosition> =
combine(
_forceLargeSize,
+ promptKind,
displayStateInteractor.isLargeScreen,
displayStateInteractor.currentRotation,
- ) { forceLarge, isLargeScreen, rotation ->
+ modalities
+ ) { forceLarge, promptKind, isLargeScreen, rotation, modalities ->
when {
forceLarge ||
isLargeScreen ||
- promptKind.value.isOnePaneNoSensorLandscapeBiometric() ->
- PromptPosition.Bottom
+ promptKind.isOnePaneNoSensorLandscapeBiometric() -> PromptPosition.Bottom
rotation == DisplayRotation.ROTATION_90 -> PromptPosition.Right
rotation == DisplayRotation.ROTATION_270 -> PromptPosition.Left
- rotation == DisplayRotation.ROTATION_180 -> PromptPosition.Top
+ rotation == DisplayRotation.ROTATION_180 && modalities.hasUdfps ->
+ PromptPosition.Top
else -> PromptPosition.Bottom
}
}
@@ -306,6 +310,10 @@
context.resources.getDimensionPixelSize(
R.dimen.biometric_prompt_two_pane_udfps_horizontal_guideline_padding
)
+ private val udfpsHorizontalShorterGuidelinePadding =
+ context.resources.getDimensionPixelSize(
+ R.dimen.biometric_prompt_two_pane_udfps_shorter_horizontal_guideline_padding
+ )
private val mediumTopGuidelinePadding =
context.resources.getDimensionPixelSize(
R.dimen.biometric_prompt_one_pane_medium_top_guideline_padding
@@ -362,7 +370,14 @@
landscapeMediumBottomPadding
)
}
- PromptPosition.Top -> Rect()
+ PromptPosition.Top ->
+ if (size.isSmall) {
+ Rect(0, 0, 0, portraitSmallBottomPadding)
+ } else if (size.isMedium && modalities.hasUdfps) {
+ Rect(0, 0, 0, sensorBounds.bottom)
+ } else {
+ Rect(0, 0, 0, portraitMediumBottomPadding)
+ }
}
}
.distinctUntilChanged()
@@ -440,47 +455,6 @@
}
}
- /**
- * Rect for positioning prompt guidelines (left, top, right, unused)
- *
- * Negative values are used to signify that guideline measuring should be flipped, measuring
- * from opposite side of the screen
- */
- val guidelineBounds: Flow<Rect> =
- combine(iconPosition, promptKind, size, position, modalities) {
- _,
- promptKind,
- size,
- position,
- modalities ->
- when (position) {
- PromptPosition.Bottom ->
- if (promptKind.isOnePaneNoSensorLandscapeBiometric()) {
- Rect(0, 0, 0, 0)
- } else {
- Rect(0, mediumTopGuidelinePadding, 0, 0)
- }
- PromptPosition.Right ->
- if (size.isSmall) {
- Rect(-smallHorizontalGuidelinePadding, 0, 0, 0)
- } else if (modalities.hasUdfps) {
- Rect(udfpsHorizontalGuidelinePadding, 0, 0, 0)
- } else {
- Rect(-mediumHorizontalGuidelinePadding, 0, 0, 0)
- }
- PromptPosition.Left ->
- if (size.isSmall) {
- Rect(0, 0, -smallHorizontalGuidelinePadding, 0)
- } else if (modalities.hasUdfps) {
- Rect(0, 0, udfpsHorizontalGuidelinePadding, 0)
- } else {
- Rect(0, 0, -mediumHorizontalGuidelinePadding, 0)
- }
- PromptPosition.Top -> Rect()
- }
- }
- .distinctUntilChanged()
-
/** Padding for prompt UI elements */
val promptPadding: Flow<Rect> =
combine(size, displayStateInteractor.currentRotation) { size, rotation ->
@@ -504,7 +478,6 @@
.map {
when {
!(customBiometricPrompt() && constraintBp()) || it == null -> null
- it.logoRes != -1 -> context.resources.getDrawable(it.logoRes, context.theme)
it.logoBitmap != null -> BitmapDrawable(context.resources, it.logoBitmap)
else -> context.getUserBadgedIcon(it, iconProvider, activityTaskManager)
}
@@ -548,6 +521,81 @@
if (contentView == null) description else ""
}
+ private val hasOnlyOneLineTitle: Flow<Boolean> =
+ combine(title, subtitle, contentView, description) {
+ title,
+ subtitle,
+ contentView,
+ description ->
+ if (subtitle.isNotEmpty() || contentView != null || description.isNotEmpty()) {
+ false
+ } else {
+ val maxWidth =
+ context.resources.getDimensionPixelSize(
+ R.dimen.biometric_prompt_two_pane_udfps_shorter_content_width
+ )
+ val attributes =
+ context.obtainStyledAttributes(
+ R.style.TextAppearance_AuthCredential_Title,
+ intArrayOf(android.R.attr.textSize)
+ )
+ val paint = TextPaint()
+ paint.textSize = attributes.getDimensionPixelSize(0, 0).toFloat()
+ val textWidth = paint.measureText(title)
+ attributes.recycle()
+ textWidth / maxWidth <= 1
+ }
+ }
+
+ /**
+ * Rect for positioning prompt guidelines (left, top, right, unused)
+ *
+ * Negative values are used to signify that guideline measuring should be flipped, measuring
+ * from opposite side of the screen
+ */
+ val guidelineBounds: Flow<Rect> =
+ combine(iconPosition, promptKind, size, position, modalities, hasOnlyOneLineTitle) {
+ _,
+ promptKind,
+ size,
+ position,
+ modalities,
+ hasOnlyOneLineTitle ->
+ var left = 0
+ var top = 0
+ var right = 0
+ when (position) {
+ PromptPosition.Bottom -> {
+ val noSensorLandscape = promptKind.isOnePaneNoSensorLandscapeBiometric()
+ top = if (noSensorLandscape) 0 else mediumTopGuidelinePadding
+ }
+ PromptPosition.Right ->
+ left = getHorizontalPadding(size, modalities, hasOnlyOneLineTitle)
+ PromptPosition.Left ->
+ right = getHorizontalPadding(size, modalities, hasOnlyOneLineTitle)
+ PromptPosition.Top -> {}
+ }
+ Rect(left, top, right, 0)
+ }
+ .distinctUntilChanged()
+
+ private fun getHorizontalPadding(
+ size: PromptSize,
+ modalities: BiometricModalities,
+ hasOnlyOneLineTitle: Boolean
+ ) =
+ if (size.isSmall) {
+ -smallHorizontalGuidelinePadding
+ } else if (modalities.hasUdfps) {
+ if (hasOnlyOneLineTitle) {
+ -udfpsHorizontalShorterGuidelinePadding
+ } else {
+ udfpsHorizontalGuidelinePadding
+ }
+ } else {
+ -mediumHorizontalGuidelinePadding
+ }
+
/** If the indicator (help, error) message should be shown. */
val isIndicatorMessageVisible: Flow<Boolean> =
combine(
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogLogger.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogLogger.kt
index c30aea0..72312b8 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogLogger.kt
@@ -16,6 +16,7 @@
package com.android.systemui.bluetooth.qsdialog
+import android.bluetooth.BluetoothDevice
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel.DEBUG
import com.android.systemui.log.dagger.BluetoothTileDialogLog
@@ -103,4 +104,29 @@
fun logDeviceUiUpdate(duration: Long) =
logBuffer.log(TAG, DEBUG, { long1 = duration }, { "DeviceUiUpdate. duration=$long1" })
+
+ fun logDeviceClickInAudioSharingWhenEnabled(inAudioSharing: Boolean) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { str1 = inAudioSharing.toString() },
+ { "DeviceClick. in audio sharing=$str1" }
+ )
+ }
+
+ fun logConnectedLeByGroupId(map: Map<Int, List<BluetoothDevice>>) {
+ logBuffer.log(TAG, DEBUG, { str1 = map.toString() }, { "ConnectedLeByGroupId. map=$str1" })
+ }
+
+ fun logLaunchSettingsCriteriaMatched(criteria: String, deviceItem: DeviceItem) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ str1 = criteria
+ str2 = deviceItem.toString()
+ },
+ { "$str1. deviceItem=$str2" }
+ )
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt
index b592b8e..4a358c0 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt
@@ -35,7 +35,17 @@
CONNECTED_OTHER_DEVICE_DISCONNECT(1508),
@UiEvent(doc = "The auto on toggle is clicked") BLUETOOTH_AUTO_ON_TOGGLE_CLICKED(1617),
@UiEvent(doc = "The audio sharing button is clicked")
- BLUETOOTH_AUDIO_SHARING_BUTTON_CLICKED(1700);
+ BLUETOOTH_AUDIO_SHARING_BUTTON_CLICKED(1700),
+ @UiEvent(doc = "Currently broadcasting and a LE audio supported device is clicked")
+ LAUNCH_SETTINGS_IN_SHARING_LE_DEVICE_CLICKED(1717),
+ @UiEvent(doc = "Currently broadcasting and a non-LE audio supported device is clicked")
+ LAUNCH_SETTINGS_IN_SHARING_NON_LE_DEVICE_CLICKED(1718),
+ @UiEvent(
+ doc = "Not broadcasting, having one connected, another saved LE audio device is clicked"
+ )
+ LAUNCH_SETTINGS_NOT_SHARING_SAVED_LE_DEVICE_CLICKED(1719),
+ @UiEvent(doc = "Not broadcasting, one of the two connected LE audio devices is clicked")
+ LAUNCH_SETTINGS_NOT_SHARING_CONNECTED_LE_DEVICE_CLICKED(1720);
override fun getId() = metricId
}
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt
index 9311760..4dafa93 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt
@@ -16,32 +16,87 @@
package com.android.systemui.bluetooth.qsdialog
+import android.bluetooth.BluetoothDevice
+import android.bluetooth.BluetoothProfile
+import android.content.Intent
+import android.os.Bundle
+import android.provider.Settings
import com.android.internal.logging.UiEventLogger
+import com.android.settingslib.bluetooth.A2dpProfile
+import com.android.settingslib.bluetooth.BluetoothUtils
+import com.android.settingslib.bluetooth.HeadsetProfile
+import com.android.settingslib.bluetooth.HearingAidProfile
+import com.android.settingslib.bluetooth.LeAudioProfile
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant
+import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.bluetooth.qsdialog.DeviceItemActionInteractor.LaunchSettingsCriteria.Companion.getCurrentConnectedLeByGroupId
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.phone.SystemUIDialog
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
-/** Defines interface for click handling of a DeviceItem. */
-interface DeviceItemActionInteractor {
- suspend fun onClick(deviceItem: DeviceItem, dialog: SystemUIDialog)
-}
-
@SysUISingleton
-open class DeviceItemActionInteractorImpl
+class DeviceItemActionInteractor
@Inject
constructor(
+ private val activityStarter: ActivityStarter,
+ private val dialogTransitionAnimator: DialogTransitionAnimator,
+ private val localBluetoothManager: LocalBluetoothManager?,
@Background private val backgroundDispatcher: CoroutineDispatcher,
private val logger: BluetoothTileDialogLogger,
private val uiEventLogger: UiEventLogger,
-) : DeviceItemActionInteractor {
+) {
+ private val leAudioProfile: LeAudioProfile?
+ get() = localBluetoothManager?.profileManager?.leAudioProfile
- override suspend fun onClick(deviceItem: DeviceItem, dialog: SystemUIDialog) {
+ private val assistantProfile: LocalBluetoothLeBroadcastAssistant?
+ get() = localBluetoothManager?.profileManager?.leAudioBroadcastAssistantProfile
+
+ private val launchSettingsCriteriaList: List<LaunchSettingsCriteria>
+ get() =
+ listOf(
+ InSharingClickedNoSource(localBluetoothManager, backgroundDispatcher, logger),
+ NotSharingClickedNonConnect(
+ leAudioProfile,
+ assistantProfile,
+ backgroundDispatcher,
+ logger
+ ),
+ NotSharingClickedConnected(
+ leAudioProfile,
+ assistantProfile,
+ backgroundDispatcher,
+ logger
+ )
+ )
+
+ suspend fun onClick(deviceItem: DeviceItem, dialog: SystemUIDialog) {
withContext(backgroundDispatcher) {
logger.logDeviceClick(deviceItem.cachedBluetoothDevice.address, deviceItem.type)
+ if (
+ BluetoothUtils.isAudioSharingEnabled() &&
+ localBluetoothManager != null &&
+ leAudioProfile != null &&
+ assistantProfile != null
+ ) {
+ val inAudioSharing = BluetoothUtils.isBroadcasting(localBluetoothManager)
+ logger.logDeviceClickInAudioSharingWhenEnabled(inAudioSharing)
+ val criteriaMatched =
+ launchSettingsCriteriaList.firstOrNull {
+ it.matched(inAudioSharing, deviceItem)
+ }
+ if (criteriaMatched != null) {
+ uiEventLogger.log(criteriaMatched.getClickUiEvent(deviceItem))
+ launchSettings(deviceItem.cachedBluetoothDevice.device, dialog)
+ return@withContext
+ }
+ }
deviceItem.cachedBluetoothDevice.apply {
when (deviceItem.type) {
DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE -> {
@@ -69,4 +124,184 @@
}
}
}
+
+ private fun launchSettings(device: BluetoothDevice, dialog: SystemUIDialog) {
+ val intent =
+ Intent(Settings.ACTION_BLUETOOTH_SETTINGS).apply {
+ putExtra(
+ EXTRA_SHOW_FRAGMENT_ARGUMENTS,
+ Bundle().apply {
+ putParcelable(LocalBluetoothLeBroadcast.EXTRA_BLUETOOTH_DEVICE, device)
+ }
+ )
+ }
+ intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK
+ activityStarter.postStartActivityDismissingKeyguard(
+ intent,
+ 0,
+ dialogTransitionAnimator.createActivityTransitionController(dialog)
+ )
+ }
+
+ private interface LaunchSettingsCriteria {
+ suspend fun matched(inAudioSharing: Boolean, deviceItem: DeviceItem): Boolean
+
+ suspend fun getClickUiEvent(deviceItem: DeviceItem): BluetoothTileDialogUiEvent
+
+ companion object {
+ suspend fun getCurrentConnectedLeByGroupId(
+ leAudioProfile: LeAudioProfile,
+ assistantProfile: LocalBluetoothLeBroadcastAssistant,
+ @Background backgroundDispatcher: CoroutineDispatcher,
+ logger: BluetoothTileDialogLogger,
+ ): Map<Int, List<BluetoothDevice>> {
+ return withContext(backgroundDispatcher) {
+ assistantProfile
+ .getDevicesMatchingConnectionStates(
+ intArrayOf(BluetoothProfile.STATE_CONNECTED)
+ )
+ ?.filterNotNull()
+ ?.groupBy { leAudioProfile.getGroupId(it) }
+ ?.also { logger.logConnectedLeByGroupId(it) } ?: emptyMap()
+ }
+ }
+ }
+ }
+
+ private class InSharingClickedNoSource(
+ private val localBluetoothManager: LocalBluetoothManager?,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+ private val logger: BluetoothTileDialogLogger,
+ ) : LaunchSettingsCriteria {
+ // If currently broadcasting and the clicked device is not connected to the source
+ override suspend fun matched(inAudioSharing: Boolean, deviceItem: DeviceItem): Boolean {
+ return withContext(backgroundDispatcher) {
+ val matched =
+ inAudioSharing &&
+ deviceItem.isMediaDevice &&
+ !BluetoothUtils.hasConnectedBroadcastSource(
+ deviceItem.cachedBluetoothDevice,
+ localBluetoothManager
+ )
+
+ if (matched) {
+ logger.logLaunchSettingsCriteriaMatched("InSharingClickedNoSource", deviceItem)
+ }
+
+ matched
+ }
+ }
+
+ override suspend fun getClickUiEvent(deviceItem: DeviceItem) =
+ if (deviceItem.isLeAudioSupported)
+ BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_IN_SHARING_LE_DEVICE_CLICKED
+ else BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_IN_SHARING_NON_LE_DEVICE_CLICKED
+ }
+
+ private class NotSharingClickedNonConnect(
+ private val leAudioProfile: LeAudioProfile?,
+ private val assistantProfile: LocalBluetoothLeBroadcastAssistant?,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+ private val logger: BluetoothTileDialogLogger,
+ ) : LaunchSettingsCriteria {
+ // If not broadcasting, having one device connected, and clicked on a not yet connected LE
+ // audio device
+ override suspend fun matched(inAudioSharing: Boolean, deviceItem: DeviceItem): Boolean {
+ return withContext(backgroundDispatcher) {
+ val matched =
+ leAudioProfile?.let { leAudio ->
+ assistantProfile?.let { assistant ->
+ !inAudioSharing &&
+ getCurrentConnectedLeByGroupId(
+ leAudio,
+ assistant,
+ backgroundDispatcher,
+ logger
+ )
+ .size == 1 &&
+ deviceItem.isNotConnectedLeAudioSupported
+ }
+ } ?: false
+
+ if (matched) {
+ logger.logLaunchSettingsCriteriaMatched(
+ "NotSharingClickedNonConnect",
+ deviceItem
+ )
+ }
+
+ matched
+ }
+ }
+
+ override suspend fun getClickUiEvent(deviceItem: DeviceItem) =
+ BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_NOT_SHARING_SAVED_LE_DEVICE_CLICKED
+ }
+
+ private class NotSharingClickedConnected(
+ private val leAudioProfile: LeAudioProfile?,
+ private val assistantProfile: LocalBluetoothLeBroadcastAssistant?,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+ private val logger: BluetoothTileDialogLogger,
+ ) : LaunchSettingsCriteria {
+ // If not broadcasting, having two device connected, clicked on any connected LE audio
+ // devices
+ override suspend fun matched(inAudioSharing: Boolean, deviceItem: DeviceItem): Boolean {
+ return withContext(backgroundDispatcher) {
+ val matched =
+ leAudioProfile?.let { leAudio ->
+ assistantProfile?.let { assistant ->
+ !inAudioSharing &&
+ getCurrentConnectedLeByGroupId(
+ leAudio,
+ assistant,
+ backgroundDispatcher,
+ logger
+ )
+ .size == 2 &&
+ deviceItem.isActiveOrConnectedLeAudioSupported
+ }
+ } ?: false
+
+ if (matched) {
+ logger.logLaunchSettingsCriteriaMatched(
+ "NotSharingClickedConnected",
+ deviceItem
+ )
+ }
+
+ matched
+ }
+ }
+
+ override suspend fun getClickUiEvent(deviceItem: DeviceItem) =
+ BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_NOT_SHARING_CONNECTED_LE_DEVICE_CLICKED
+ }
+
+ private companion object {
+ const val EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args"
+
+ val DeviceItem.isLeAudioSupported: Boolean
+ get() =
+ cachedBluetoothDevice.profiles.any { profile ->
+ profile is LeAudioProfile && profile.isEnabled(cachedBluetoothDevice.device)
+ }
+
+ val DeviceItem.isNotConnectedLeAudioSupported: Boolean
+ get() = type == DeviceItemType.SAVED_BLUETOOTH_DEVICE && isLeAudioSupported
+
+ val DeviceItem.isActiveOrConnectedLeAudioSupported: Boolean
+ get() =
+ (type == DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE ||
+ type == DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE) && isLeAudioSupported
+
+ val DeviceItem.isMediaDevice: Boolean
+ get() =
+ cachedBluetoothDevice.connectableProfiles.any {
+ it is A2dpProfile ||
+ it is HearingAidProfile ||
+ it is LeAudioProfile ||
+ it is HeadsetProfile
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt
index 8993a3b..38f51da 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt
@@ -19,23 +19,25 @@
import android.annotation.SuppressLint
import android.app.DreamManager
import com.android.systemui.CoreStartable
-import com.android.systemui.Flags.glanceableHubAllowKeyguardWhenDreaming
import com.android.systemui.Flags.communalHub
+import com.android.systemui.Flags.glanceableHubAllowKeyguardWhenDreaming
import com.android.systemui.Flags.restartDreamOnUnocclude
-import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.filterState
import com.android.systemui.power.domain.interactor.PowerInteractor
-import com.android.systemui.util.kotlin.Utils.Companion.sample
-import com.android.systemui.util.kotlin.sample
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.util.kotlin.Utils.Companion.sampleFilter
+import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
-import javax.inject.Inject
/**
* A [CoreStartable] responsible for automatically starting the dream when the communal hub is
@@ -48,7 +50,6 @@
private val powerInteractor: PowerInteractor,
private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
- private val communalInteractor: CommunalInteractor,
private val dreamManager: DreamManager,
@Background private val bgScope: CoroutineScope,
) : CoreStartable {
@@ -60,31 +61,28 @@
// Return to dream from occluded when not already dreaming.
if (restartDreamOnUnocclude()) {
- keyguardTransitionInteractor.startedKeyguardTransitionStep
- .sample(keyguardInteractor.isDreaming, ::Pair)
- .filter {
- it.first.from == KeyguardState.OCCLUDED &&
- it.first.to == KeyguardState.DREAMING &&
- !it.second
- }
+ keyguardTransitionInteractor
+ .transition(Edge.create(from = KeyguardState.OCCLUDED, to = KeyguardState.DREAMING))
+ .filterState(TransitionState.STARTED)
+ .sampleFilter(keyguardInteractor.isDreaming) { isDreaming -> !isDreaming }
.onEach { dreamManager.startDream() }
.launchIn(bgScope)
}
// Restart the dream underneath the hub in order to support the ability to swipe
// away the hub to enter the dream.
- keyguardTransitionInteractor.finishedKeyguardState
- .sample(powerInteractor.isAwake, keyguardInteractor.isDreaming)
- .onEach { (finishedState, isAwake, dreaming) ->
- if (
- finishedState == KeyguardState.GLANCEABLE_HUB &&
- !dreaming &&
- !glanceableHubAllowKeyguardWhenDreaming() &&
- dreamManager.canStartDreaming(isAwake)
- ) {
- dreamManager.startDream()
- }
+ keyguardTransitionInteractor
+ .transition(
+ edge = Edge.create(to = Scenes.Communal),
+ edgeWithoutSceneContainer = Edge.create(to = KeyguardState.GLANCEABLE_HUB)
+ )
+ .filterState(TransitionState.FINISHED)
+ .sampleFilter(powerInteractor.isAwake) { isAwake ->
+ dreamManager.canStartDreaming(isAwake)
}
+ .sampleFilter(keyguardInteractor.isDreaming) { isDreaming -> !isDreaming }
+ .filter { !glanceableHubAllowKeyguardWhenDreaming() }
+ .onEach { dreamManager.startDream() }
.launchIn(bgScope)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
index d522c7e..88c3f9f6 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
@@ -208,7 +208,10 @@
// doing any translation.
CommunalScenes.Communal
}
- to == KeyguardState.GONE -> CommunalScenes.Blank
+ // Transitioning to Blank scene when entering the edit mode will be handled separately
+ // with custom animations.
+ to == KeyguardState.GONE && !communalInteractor.editModeOpen.value ->
+ CommunalScenes.Blank
!docked && !KeyguardState.deviceIsAwakeInState(to) -> {
// If the user taps the screen and wakes the device within this timeout, we don't
// want to dismiss the hub
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt
index 260dcba..7a4006d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt
@@ -28,6 +28,7 @@
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
@@ -51,7 +52,7 @@
fun changeScene(toScene: SceneKey, transitionKey: TransitionKey? = null)
/** Immediately snaps to the desired scene. */
- fun snapToScene(toScene: SceneKey)
+ fun snapToScene(toScene: SceneKey, delayMillis: Long = 0)
/**
* Updates the transition state of the hub [SceneTransitionLayout].
@@ -92,10 +93,11 @@
}
}
- override fun snapToScene(toScene: SceneKey) {
+ override fun snapToScene(toScene: SceneKey, delayMillis: Long) {
applicationScope.launch {
// SceneTransitionLayout state updates must be triggered on the thread the STL was
// created on.
+ delay(delayMillis)
sceneDataSource.snapToScene(toScene)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index fdb40fb..00678a8 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -38,6 +38,7 @@
import com.android.systemui.communal.shared.model.CommunalContentSize.THIRD
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
+import com.android.systemui.communal.shared.model.EditModeState
import com.android.systemui.communal.widgets.CommunalAppWidgetHost
import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
import com.android.systemui.communal.widgets.WidgetConfigurator
@@ -46,6 +47,7 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
@@ -164,7 +166,7 @@
/** Whether to start dreaming when returning from occluded */
val dreamFromOccluded: Flow<Boolean> =
keyguardTransitionInteractor
- .transitionStepsToState(KeyguardState.OCCLUDED)
+ .transition(Edge.create(to = KeyguardState.OCCLUDED))
.map { it.from == KeyguardState.DREAMING }
.stateIn(scope = applicationScope, SharingStarted.Eagerly, false)
@@ -306,6 +308,7 @@
preselectedKey: String? = null,
shouldOpenWidgetPickerOnStart: Boolean = false,
) {
+ communalSceneInteractor.setEditModeState(EditModeState.STARTING)
editWidgetsActivityStarter.startActivity(preselectedKey, shouldOpenWidgetPickerOnStart)
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
index 5cfe979..20d8a2a 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
@@ -22,14 +22,17 @@
import com.android.systemui.communal.data.repository.CommunalSceneRepository
import com.android.systemui.communal.domain.model.CommunalTransitionProgressModel
import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.communal.shared.model.EditModeState
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
@@ -53,8 +56,18 @@
}
/** Immediately snaps to the new scene. */
- fun snapToScene(newScene: SceneKey) {
- communalSceneRepository.snapToScene(newScene)
+ fun snapToScene(newScene: SceneKey, delayMillis: Long = 0) {
+ communalSceneRepository.snapToScene(newScene, delayMillis)
+ }
+
+ /** Immediately snaps to the new scene when activity is started. */
+ fun snapToSceneForActivityStart(newScene: SceneKey, delayMillis: Long = 0) {
+ // skip if we're starting edit mode activity, as it will be handled later by changeScene
+ // with transition key [CommunalTransitionKeys.ToEditMode].
+ if (_editModeState.value == EditModeState.STARTING) {
+ return
+ }
+ snapToScene(newScene, delayMillis)
}
/**
@@ -62,6 +75,17 @@
*/
val currentScene: Flow<SceneKey> = communalSceneRepository.currentScene
+ private val _editModeState = MutableStateFlow<EditModeState?>(null)
+ /**
+ * Current state for glanceable hub edit mode, used to chain the animations when transitioning
+ * between communal scene and the edit mode activity.
+ */
+ val editModeState = _editModeState.asStateFlow()
+
+ fun setEditModeState(value: EditModeState?) {
+ _editModeState.value = value
+ }
+
/** Transition state of the hub mode. */
val transitionState: StateFlow<ObservableTransitionState> =
communalSceneRepository.transitionState
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt
index 73cfb52..11fb233 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt
@@ -26,6 +26,10 @@
object CommunalTransitionKeys {
/** Fades the glanceable hub without any translation */
val SimpleFade = TransitionKey("SimpleFade")
+ /** Transition from the glanceable hub before entering edit mode */
+ val ToEditMode = TransitionKey("ToEditMode")
+ /** Transition to the glanceable hub after exiting edit mode */
+ val FromEditMode = TransitionKey("FromEditMode")
/** Immediately transitions without any delay */
val Immediately = TransitionKey("Immediately")
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/EditModeState.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/EditModeState.kt
new file mode 100644
index 0000000..ace9c2e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/EditModeState.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.shared.model
+
+/**
+ * Models the state of the edit mode activity. Used to chain the animation during the transition
+ * between the hub on communal scene, and the edit mode activity after unlocking the keyguard.
+ */
+enum class EditModeState(val value: Int) {
+ // starting activity after dismissing keyguard
+ STARTING(0),
+ // activity content is showing
+ SHOWING(1),
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandler.kt
new file mode 100644
index 0000000..a88b777
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandler.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.smartspace
+
+import android.app.ActivityOptions
+import android.app.PendingIntent
+import android.content.Intent
+import android.view.View
+import android.widget.RemoteViews
+import com.android.systemui.animation.ActivityTransitionAnimator
+import com.android.systemui.communal.util.InteractionHandlerDelegate
+import com.android.systemui.communal.widgets.SmartspaceAppWidgetHostView
+import com.android.systemui.plugins.ActivityStarter
+import javax.inject.Inject
+
+/**
+ * Handles interactions on smartspace elements on the hub.
+ */
+class SmartspaceInteractionHandler @Inject constructor(
+ private val activityStarter: ActivityStarter,
+) : RemoteViews.InteractionHandler {
+ private val delegate = InteractionHandlerDelegate(
+ findViewToAnimate = { view -> view is SmartspaceAppWidgetHostView },
+ intentStarter = this::startIntent,
+ )
+
+ override fun onInteraction(
+ view: View,
+ pendingIntent: PendingIntent,
+ response: RemoteViews.RemoteResponse
+ ): Boolean = delegate.onInteraction(view, pendingIntent, response)
+
+ private fun startIntent(
+ pendingIntent: PendingIntent,
+ fillInIntent: Intent,
+ extraOptions: ActivityOptions,
+ animationController: ActivityTransitionAnimator.Controller?
+ ): Boolean {
+ activityStarter.startPendingIntentWithoutDismissing(
+ pendingIntent,
+ /* dismissShade = */ false,
+ /* intentSentUiThreadCallback = */ null,
+ animationController,
+ fillInIntent,
+ extraOptions.toBundle()
+ )
+ return true
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index 8cd5603..cc90730 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -25,6 +25,7 @@
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.shared.model.EditModeState
import com.android.systemui.communal.widgets.WidgetConfigurator
import com.android.systemui.media.controls.ui.view.MediaHost
import kotlinx.coroutines.flow.Flow
@@ -34,12 +35,15 @@
/** The base view model for the communal hub. */
abstract class BaseCommunalViewModel(
- private val communalSceneInteractor: CommunalSceneInteractor,
+ val communalSceneInteractor: CommunalSceneInteractor,
private val communalInteractor: CommunalInteractor,
val mediaHost: MediaHost,
) {
val currentScene: Flow<SceneKey> = communalSceneInteractor.currentScene
+ /** Used to animate showing or hiding the communal content. */
+ open val isCommunalContentVisible: Flow<Boolean> = MutableStateFlow(false)
+
/** Whether communal hub should be focused by accessibility tools. */
open val isFocusable: Flow<Boolean> = MutableStateFlow(false)
@@ -63,6 +67,8 @@
communalSceneInteractor.changeScene(scene, transitionKey)
}
+ fun setEditModeState(state: EditModeState?) = communalSceneInteractor.setEditModeState(state)
+
/**
* Updates the transition state of the hub [SceneTransitionLayout].
*
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
index bc65ccb..c0c5861 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
@@ -24,25 +24,33 @@
import android.util.Log
import androidx.activity.result.ActivityResultLauncher
import com.android.internal.logging.UiEventLogger
+import com.android.systemui.Flags.enableWidgetPickerSizeFilter
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.log.CommunalUiEvent
+import com.android.systemui.communal.shared.model.EditModeState
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.log.dagger.CommunalLog
import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.media.dagger.MediaModule
+import com.android.systemui.res.R
+import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
import javax.inject.Inject
import javax.inject.Named
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.withContext
@@ -54,6 +62,7 @@
communalSceneInteractor: CommunalSceneInteractor,
private val communalInteractor: CommunalInteractor,
private val communalSettingsInteractor: CommunalSettingsInteractor,
+ keyguardTransitionInteractor: KeyguardTransitionInteractor,
@Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost,
private val uiEventLogger: UiEventLogger,
@CommunalLog logBuffer: LogBuffer,
@@ -64,6 +73,20 @@
override val isEditMode = true
+ override val isCommunalContentVisible: Flow<Boolean> =
+ communalSceneInteractor.editModeState.map { it == EditModeState.SHOWING }
+
+ /**
+ * Emits when edit mode activity can show, after we've transitioned to [KeyguardState.GONE]
+ * and edit mode is open.
+ */
+ val canShowEditMode =
+ allOf(
+ keyguardTransitionInteractor.isFinishedInState(KeyguardState.GONE),
+ communalInteractor.editModeOpen
+ )
+ .filter { it }
+
// Only widgets are editable.
override val communalContent: Flow<List<CommunalContentModel>> =
communalInteractor.widgetContent.onEach { models ->
@@ -138,6 +161,16 @@
return Intent(Intent.ACTION_PICK).apply {
setPackage(packageName)
+ if (enableWidgetPickerSizeFilter()) {
+ putExtra(
+ EXTRA_DESIRED_WIDGET_WIDTH,
+ resources.getDimensionPixelSize(R.dimen.communal_widget_picker_desired_width)
+ )
+ putExtra(
+ EXTRA_DESIRED_WIDGET_HEIGHT,
+ resources.getDimensionPixelSize(R.dimen.communal_widget_picker_desired_height)
+ )
+ }
putExtra(
AppWidgetManager.EXTRA_CATEGORY_FILTER,
communalSettingsInteractor.communalWidgetCategories.value
@@ -160,9 +193,16 @@
/** Sets whether edit mode is currently open */
fun setEditModeOpen(isOpen: Boolean) = communalInteractor.setEditModeOpen(isOpen)
+ /** Called when exiting the edit mode, before transitioning back to the communal scene. */
+ fun cleanupEditModeState() {
+ communalSceneInteractor.setEditModeState(null)
+ }
+
companion object {
private const val TAG = "CommunalEditModeViewModel"
+ private const val EXTRA_DESIRED_WIDGET_WIDTH = "desired_widget_width"
+ private const val EXTRA_DESIRED_WIDGET_HEIGHT = "desired_widget_height"
private const val EXTRA_UI_SURFACE_KEY = "ui_surface"
private const val EXTRA_UI_SURFACE_VALUE = "widgets_hub"
const val EXTRA_ADDED_APP_WIDGETS_KEY = "added_app_widgets"
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt
index 9114aab..fce18a26 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt
@@ -18,20 +18,23 @@
import android.graphics.Color
import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.communal.util.CommunalColors
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.ui.viewmodel.DreamingToGlanceableHubTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToDreamingTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToGlanceableHubTransitionViewModel
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
@@ -48,22 +51,27 @@
dreamToGlanceableHubTransitionViewModel: DreamingToGlanceableHubTransitionViewModel,
glanceableHubToDreamTransitionViewModel: GlanceableHubToDreamingTransitionViewModel,
communalInteractor: CommunalInteractor,
- keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ communalSceneInteractor: CommunalSceneInteractor,
+ keyguardTransitionInteractor: KeyguardTransitionInteractor
) {
// Show UMO on glanceable hub immediately on transition into glanceable hub
private val showUmoFromOccludedToGlanceableHub: Flow<Boolean> =
keyguardTransitionInteractor
- .transitionStepsFromState(KeyguardState.OCCLUDED)
+ .transition(
+ Edge.create(from = KeyguardState.OCCLUDED, to = KeyguardState.GLANCEABLE_HUB)
+ )
.filter {
- it.to == KeyguardState.GLANCEABLE_HUB &&
- (it.transitionState == TransitionState.STARTED ||
- it.transitionState == TransitionState.CANCELED)
+ (it.transitionState == TransitionState.STARTED ||
+ it.transitionState == TransitionState.CANCELED)
}
.map { it.transitionState == TransitionState.STARTED }
private val showUmoFromGlanceableHubToOccluded: Flow<Boolean> =
keyguardTransitionInteractor
- .transitionStepsFromState(KeyguardState.GLANCEABLE_HUB)
+ .transition(
+ edge = Edge.create(from = Scenes.Communal),
+ edgeWithoutSceneContainer = Edge.create(from = KeyguardState.GLANCEABLE_HUB)
+ )
.filter {
it.to == KeyguardState.OCCLUDED &&
(it.transitionState == TransitionState.FINISHED ||
@@ -77,7 +85,13 @@
* of UMO should be updated.
*/
val isUmoOnCommunal: Flow<Boolean> =
- merge(
+ allOf(
+ // Only show UMO on the hub if the hub is at least partially visible. This prevents
+ // the UMO from being missing on the lock screen when going from the hub to lock
+ // screen in some way other than through a direct transition, such as unlocking from
+ // the hub, then pressing power twice to go back to the lock screen.
+ communalSceneInteractor.isCommunalVisible,
+ merge(
lockscreenToGlanceableHubTransitionViewModel.showUmo,
glanceableHubToLockscreenTransitionViewModel.showUmo,
dreamToGlanceableHubTransitionViewModel.showUmo,
@@ -85,17 +99,18 @@
showUmoFromOccludedToGlanceableHub,
showUmoFromGlanceableHubToOccluded,
)
- .distinctUntilChanged()
+ )
/** Whether to show communal when exiting the occluded state. */
val showCommunalFromOccluded: Flow<Boolean> = communalInteractor.showCommunalFromOccluded
val transitionFromOccludedEnded =
- keyguardTransitionInteractor.transitionStepsFromState(KeyguardState.OCCLUDED).filter { step
- ->
- step.transitionState == TransitionState.FINISHED ||
- step.transitionState == TransitionState.CANCELED
- }
+ keyguardTransitionInteractor
+ .transition(Edge.create(from = KeyguardState.OCCLUDED))
+ .filter { step ->
+ step.transitionState == TransitionState.FINISHED ||
+ step.transitionState == TransitionState.CANCELED
+ }
val recentsBackgroundColor: Flow<Color?> =
combine(showCommunalFromOccluded, communalColors.backgroundColor) {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index c6fa5a84..11247df 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
@@ -125,6 +125,8 @@
logger.d({ "Content updated: $str1" }) { str1 = models.joinToString { it.key } }
}
+ override val isCommunalContentVisible: Flow<Boolean> = MutableStateFlow(true)
+
/**
* Freeze the content flow, when an activity is about to show, like starting a timer via voice:
* 1) in handheld mode, use the keyguard occluded state;
diff --git a/packages/SystemUI/src/com/android/systemui/communal/util/InteractionHandlerDelegate.kt b/packages/SystemUI/src/com/android/systemui/communal/util/InteractionHandlerDelegate.kt
new file mode 100644
index 0000000..40b182d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/util/InteractionHandlerDelegate.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.util
+
+import android.app.ActivityOptions
+import android.app.PendingIntent
+import android.content.Intent
+import android.view.View
+import android.widget.RemoteViews
+import androidx.core.util.component1
+import androidx.core.util.component2
+import com.android.systemui.animation.ActivityTransitionAnimator
+
+
+/** A delegate that can be used to launch activities from [RemoteViews] */
+class InteractionHandlerDelegate(
+ private val findViewToAnimate: (View) -> Boolean,
+ private val intentStarter: IntentStarter,
+) : RemoteViews.InteractionHandler {
+
+ /**
+ * Responsible for starting the pending intent for launching activities.
+ */
+ fun interface IntentStarter {
+ fun startPendingIntent(
+ intent: PendingIntent,
+ fillInIntent: Intent,
+ activityOptions: ActivityOptions,
+ controller: ActivityTransitionAnimator.Controller?,
+ ): Boolean
+ }
+
+ override fun onInteraction(
+ view: View,
+ pendingIntent: PendingIntent,
+ response: RemoteViews.RemoteResponse
+ ): Boolean {
+ val launchOptions = response.getLaunchOptions(view)
+ return when {
+ pendingIntent.isActivity -> {
+ // Forward the fill-in intent and activity options retrieved from the response
+ // to populate the pending intent, so that list items can launch respective
+ // activities.
+ val hostView = getNearestParent(view)
+ val animationController =
+ hostView?.let(ActivityTransitionAnimator.Controller::fromView)
+ val (fillInIntent, activityOptions) = launchOptions
+ intentStarter.startPendingIntent(
+ pendingIntent,
+ fillInIntent,
+ activityOptions,
+ animationController
+ )
+ }
+
+ else -> RemoteViews.startPendingIntent(view, pendingIntent, launchOptions)
+ }
+ }
+
+ private fun getNearestParent(child: View): View? {
+ var view: Any? = child
+ while (view is View) {
+ if (findViewToAnimate(view)) return view
+ view = view.parent
+ }
+ return null
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
index 426f484..40df6cec 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -37,6 +37,7 @@
import com.android.systemui.communal.shared.log.CommunalUiEvent
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalTransitionKeys
+import com.android.systemui.communal.shared.model.EditModeState
import com.android.systemui.communal.ui.compose.CommunalHub
import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
import com.android.systemui.communal.util.WidgetPickerIntentUtils.getWidgetExtraFromIntent
@@ -70,8 +71,6 @@
private var shouldOpenWidgetPickerOnStart = false
- private var lockOnDestroy = false
-
private val addWidgetActivityLauncher: ActivityResultLauncher<Intent> =
registerForActivityResult(StartActivityForResult()) { result ->
when (result.resultCode) {
@@ -97,8 +96,7 @@
run { Log.w(TAG, "No AppWidgetProviderInfo found in result.") }
}
}
- }
- ?: run { Log.w(TAG, "No data in result.") }
+ } ?: run { Log.w(TAG, "No data in result.") }
}
else ->
Log.w(
@@ -110,6 +108,7 @@
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ listenForTransitionAndChangeScene()
communalViewModel.setEditModeOpen(true)
@@ -141,6 +140,22 @@
}
}
+ // Handle scene change to show the activity and animate in its content
+ private fun listenForTransitionAndChangeScene() {
+ lifecycleScope.launch {
+ communalViewModel.canShowEditMode.collect {
+ communalViewModel.changeScene(
+ CommunalScenes.Blank,
+ CommunalTransitionKeys.ToEditMode
+ )
+ // wait till transitioned to Blank scene, then animate in communal content in
+ // edit mode
+ communalViewModel.currentScene.first { it == CommunalScenes.Blank }
+ communalViewModel.setEditModeState(EditModeState.SHOWING)
+ }
+ }
+ }
+
private fun onOpenWidgetPicker() {
lifecycleScope.launch {
communalViewModel.onOpenWidgetPicker(
@@ -153,16 +168,18 @@
private fun onEditDone() {
lifecycleScope.launch {
+ communalViewModel.cleanupEditModeState()
+
communalViewModel.changeScene(
CommunalScenes.Communal,
- CommunalTransitionKeys.SimpleFade
+ CommunalTransitionKeys.FromEditMode
)
// Wait for the current scene to be idle on communal.
communalViewModel.isIdleOnCommunal.first { it }
- // Then finish the activity (this helps to avoid a flash of lockscreen when locking
- // in onDestroy()).
- lockOnDestroy = true
+
+ // Lock to go back to the hub after exiting.
+ lockNow()
finish()
}
}
@@ -195,9 +212,8 @@
override fun onDestroy() {
super.onDestroy()
+ communalViewModel.cleanupEditModeState()
communalViewModel.setEditModeOpen(false)
-
- if (lockOnDestroy) lockNow()
}
private fun lockNow() {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/SmartspaceAppWidgetHostView.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/SmartspaceAppWidgetHostView.kt
new file mode 100644
index 0000000..7f11463
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/SmartspaceAppWidgetHostView.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.widgets
+
+import android.appwidget.AppWidgetHostView
+import android.appwidget.AppWidgetProviderInfo
+import android.content.Context
+import com.android.systemui.animation.LaunchableView
+import com.android.systemui.animation.LaunchableViewDelegate
+
+/** AppWidgetHostView that displays in communal hub to show smartspace content. */
+class SmartspaceAppWidgetHostView(context: Context) : AppWidgetHostView(context), LaunchableView {
+ private val launchableViewDelegate =
+ LaunchableViewDelegate(
+ this,
+ superSetVisibility = { super.setVisibility(it) },
+ )
+
+ override fun setAppWidget(appWidgetId: Int, info: AppWidgetProviderInfo?) {
+ super.setAppWidget(appWidgetId, info)
+ setPadding(0, 0, 0, 0)
+ }
+
+ override fun getRemoteContextEnsuringCorrectCachedApkPath(): Context? {
+ // Silence errors
+ return null
+ }
+
+ override fun setShouldBlockVisibilityChanges(block: Boolean) =
+ launchableViewDelegate.setShouldBlockVisibilityChanges(block)
+
+ override fun setVisibility(visibility: Int) = launchableViewDelegate.setVisibility(visibility)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
index 51a3a6d..cbc6c97 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
@@ -19,53 +19,46 @@
import android.app.ActivityOptions
import android.app.PendingIntent
import android.content.Intent
-import android.util.Pair
import android.view.View
import android.widget.RemoteViews
-import androidx.core.util.component1
-import androidx.core.util.component2
import com.android.systemui.animation.ActivityTransitionAnimator
-import com.android.systemui.common.ui.view.getNearestParent
+import com.android.systemui.communal.util.InteractionHandlerDelegate
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.plugins.ActivityStarter
import javax.inject.Inject
+@SysUISingleton
class WidgetInteractionHandler
@Inject
constructor(
private val activityStarter: ActivityStarter,
) : RemoteViews.InteractionHandler {
+
+ private val delegate = InteractionHandlerDelegate(
+ findViewToAnimate = { view -> view is CommunalAppWidgetHostView },
+ intentStarter = this::startIntent,
+ )
+
override fun onInteraction(
view: View,
pendingIntent: PendingIntent,
response: RemoteViews.RemoteResponse
- ): Boolean {
- val launchOptions = response.getLaunchOptions(view)
- return when {
- pendingIntent.isActivity ->
- // Forward the fill-in intent and activity options retrieved from the response
- // to populate the pending intent, so that list items can launch respective
- // activities.
- startActivity(view, pendingIntent, launchOptions)
- else -> RemoteViews.startPendingIntent(view, pendingIntent, launchOptions)
- }
- }
+ ): Boolean = delegate.onInteraction(view, pendingIntent, response)
- private fun startActivity(
- view: View,
+
+ private fun startIntent(
pendingIntent: PendingIntent,
- launchOptions: Pair<Intent, ActivityOptions>,
+ fillInIntent: Intent,
+ extraOptions: ActivityOptions,
+ controller: ActivityTransitionAnimator.Controller?
): Boolean {
- val hostView = view.getNearestParent<CommunalAppWidgetHostView>()
- val animationController = hostView?.let(ActivityTransitionAnimator.Controller::fromView)
- val (fillInIntent, activityOptions) = launchOptions
-
activityStarter.startPendingIntentMaybeDismissingKeyguard(
pendingIntent,
/* dismissShade = */ false,
/* intentSentUiThreadCallback = */ null,
- animationController,
+ controller,
fillInIntent,
- activityOptions.toBundle(),
+ extraOptions.toBundle(),
)
return true
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/CommonSystemUIUnfoldModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/CommonSystemUIUnfoldModule.kt
new file mode 100644
index 0000000..a91ce16
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/CommonSystemUIUnfoldModule.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dagger
+
+import com.android.systemui.unfold.SysUIUnfoldComponent
+import com.android.systemui.unfold.SysUIUnfoldModule.BoundFromSysUiUnfoldModule
+import dagger.BindsOptionalOf
+import dagger.Module
+import dagger.Provides
+import java.util.Optional
+import kotlin.jvm.optionals.getOrElse
+
+
+/**
+ * Module for foldable-related classes that is available in all SystemUI variants.
+ * Provides `Optional<SysUIUnfoldComponent>` which is present when the device is a foldable
+ * device that has fold/unfold animation enabled.
+ */
+@Module
+abstract class CommonSystemUIUnfoldModule {
+
+ /* Note this will be injected as @BoundFromSysUiUnfoldModule Optional<Optional<...>> */
+ @BindsOptionalOf
+ @BoundFromSysUiUnfoldModule
+ abstract fun optionalSysUiUnfoldComponent(): Optional<SysUIUnfoldComponent>
+
+ companion object {
+ @Provides
+ @SysUISingleton
+ fun sysUiUnfoldComponent(
+ /**
+ * This will be empty when [com.android.systemui.unfold.SysUIUnfoldModule] is not part
+ * of the graph, and contain the optional when it is.
+ */
+ @BoundFromSysUiUnfoldModule
+ optionalOfOptional: Optional<Optional<SysUIUnfoldComponent>>
+ ): Optional<SysUIUnfoldComponent> = optionalOfOptional.getOrElse { Optional.empty() }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
index a431a59..b71af69 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
@@ -19,6 +19,7 @@
import com.android.systemui.keyguard.CustomizationProvider;
import com.android.systemui.statusbar.NotificationInsetsModule;
import com.android.systemui.statusbar.QsFrameTranslateModule;
+import com.android.systemui.unfold.SysUIUnfoldModule;
import dagger.Subcomponent;
@@ -34,6 +35,7 @@
SystemUIBinder.class,
SystemUIModule.class,
SystemUICoreStartableModule.class,
+ SysUIUnfoldModule.class,
ReferenceSystemUIModule.class})
public interface ReferenceSysUIComponent extends SysUIComponent {
@@ -51,3 +53,4 @@
*/
void inject(CustomizationProvider customizationProvider);
}
+
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 7aab37e..9f0fc51 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -31,6 +31,7 @@
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
import com.android.systemui.doze.DozeHost;
+import com.android.systemui.keyboard.shortcut.ShortcutHelperModule;
import com.android.systemui.keyguard.ui.composable.blueprint.DefaultBlueprintModule;
import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule;
import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSectionsModule;
@@ -137,7 +138,8 @@
UnfoldTransitionModule.Startables.class,
ToastModule.class,
VolumeModule.class,
- WallpaperModule.class
+ WallpaperModule.class,
+ ShortcutHelperModule.class,
})
public abstract class ReferenceSystemUIModule {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 2ebb94f..a7ff3c3 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -143,7 +143,6 @@
import com.android.systemui.telephony.data.repository.TelephonyRepositoryModule;
import com.android.systemui.temporarydisplay.dagger.TemporaryDisplayModule;
import com.android.systemui.tuner.dagger.TunerModule;
-import com.android.systemui.unfold.SysUIUnfoldModule;
import com.android.systemui.user.UserModule;
import com.android.systemui.user.domain.UserDomainLayerModule;
import com.android.systemui.util.EventLogModule;
@@ -254,7 +253,7 @@
SystemPropertiesFlagsModule.class,
SysUIConcurrencyModule.class,
SysUICoroutinesModule.class,
- SysUIUnfoldModule.class,
+ CommonSystemUIUnfoldModule.class,
TelephonyRepositoryModule.class,
TemporaryDisplayModule.class,
TunerModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
index 9d110e6..10d6881 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
@@ -29,6 +29,7 @@
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.util.kotlin.Quad
+import com.android.systemui.utils.coroutines.flow.mapLatestConflated
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -37,6 +38,7 @@
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
@@ -96,7 +98,16 @@
.filter { currentScene ->
currentScene == Scenes.Gone || currentScene == Scenes.Lockscreen
}
- .map { it == Scenes.Gone }
+ .mapLatestConflated { scene ->
+ if (scene == Scenes.Gone) {
+ // Make sure device unlock status is definitely unlocked before we consider the
+ // device "entered".
+ deviceUnlockedInteractor.deviceUnlockStatus.first { it.isUnlocked }
+ true
+ } else {
+ false
+ }
+ }
.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
index 4ac0c56..9d6c2bf 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
@@ -16,6 +16,7 @@
package com.android.systemui.display.data.repository
+import android.annotation.SuppressLint
import android.hardware.display.DisplayManager
import android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED
import android.hardware.display.DisplayManager.DisplayListener
@@ -27,6 +28,7 @@
import android.view.Display
import com.android.app.tracing.FlowTracing.traceEach
import com.android.app.tracing.traceSection
+import com.android.systemui.Flags
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
@@ -42,11 +44,12 @@
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.scan
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
@@ -91,6 +94,7 @@
}
@SysUISingleton
+@SuppressLint("SharedFlowCreation")
class DisplayRepositoryImpl
@Inject
constructor(
@@ -132,8 +136,50 @@
allDisplayEvents.filterIsInstance<DisplayEvent.Changed>().map { event -> event.displayId }
override val displayAdditionEvent: Flow<Display?> =
- allDisplayEvents.filterIsInstance<DisplayEvent.Added>().map {
- displayManager.getDisplay(it.displayId)
+ allDisplayEvents.filterIsInstance<DisplayEvent.Added>().map { getDisplay(it.displayId) }
+
+ private val oldEnabledDisplays: Flow<Set<Display>> =
+ allDisplayEvents
+ .map { getDisplays() }
+ .shareIn(bgApplicationScope, started = SharingStarted.WhileSubscribed(), replay = 1)
+
+ /** Propagate to the listeners only enabled displays */
+ private val enabledDisplayIds: Flow<Set<Int>> =
+ if (Flags.enableEfficientDisplayRepository()) {
+ allDisplayEvents
+ .scan(initial = emptySet()) { previousIds: Set<Int>, event: DisplayEvent ->
+ val id = event.displayId
+ when (event) {
+ is DisplayEvent.Removed -> previousIds - id
+ is DisplayEvent.Added,
+ is DisplayEvent.Changed -> previousIds + id
+ }
+ }
+ .shareIn(
+ bgApplicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ replay = 1
+ )
+ } else {
+ oldEnabledDisplays.map { enabledDisplaysSet ->
+ enabledDisplaysSet.map { it.displayId }.toSet()
+ }
+ }
+ .debugLog("enabledDisplayIds")
+
+ /**
+ * Represents displays that went though the [DisplayListener.onDisplayAdded] callback.
+ *
+ * Those are commonly the ones provided by [DisplayManager.getDisplays] by default.
+ */
+ private val enabledDisplays: Flow<Set<Display>> =
+ if (Flags.enableEfficientDisplayRepository()) {
+ enabledDisplayIds
+ .mapElementsLazily { displayId -> getDisplay(displayId) }
+ .flowOn(backgroundCoroutineDispatcher)
+ .debugLog("enabledDisplayIds")
+ } else {
+ oldEnabledDisplays
}
/**
@@ -141,35 +187,26 @@
*
* Those are commonly the ones provided by [DisplayManager.getDisplays] by default.
*/
- private val enabledDisplays =
- allDisplayEvents
- .map { getDisplays() }
- .shareIn(bgApplicationScope, started = SharingStarted.WhileSubscribed(), replay = 1)
-
override val displays: Flow<Set<Display>> = enabledDisplays
private fun getDisplays(): Set<Display> =
traceSection("$TAG#getDisplays()") { displayManager.displays?.toSet() ?: emptySet() }
- /** Propagate to the listeners only enabled displays */
- private val enabledDisplayIds: Flow<Set<Int>> =
- enabledDisplays
- .map { enabledDisplaysSet -> enabledDisplaysSet.map { it.displayId }.toSet() }
- .debugLog("enabledDisplayIds")
-
private val _ignoredDisplayIds = MutableStateFlow<Set<Int>>(emptySet())
private val ignoredDisplayIds: Flow<Set<Int>> = _ignoredDisplayIds.debugLog("ignoredDisplayIds")
private fun getInitialConnectedDisplays(): Set<Int> =
- displayManager
- .getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)
- .map { it.displayId }
- .toSet()
- .also {
- if (DEBUG) {
- Log.d(TAG, "getInitialConnectedDisplays: $it")
+ traceSection("$TAG#getInitialConnectedDisplays") {
+ displayManager
+ .getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)
+ .map { it.displayId }
+ .toSet()
+ .also {
+ if (DEBUG) {
+ Log.d(TAG, "getInitialConnectedDisplays: $it")
+ }
}
- }
+ }
/* keeps connected displays until they are disconnected. */
private val connectedDisplayIds: StateFlow<Set<Int>> =
@@ -230,6 +267,9 @@
private fun getDisplayType(displayId: Int): Int? =
traceSection("$TAG#getDisplayType") { displayManager.getDisplay(displayId)?.type }
+ private fun getDisplay(displayId: Int): Display? =
+ traceSection("$TAG#getDisplay") { displayManager.getDisplay(displayId) }
+
/**
* Pending displays are the ones connected, but not enabled and not ignored.
*
@@ -307,6 +347,30 @@
}
}
+ /**
+ * Maps a set of T to a set of V, minimizing the number of `createValue` calls taking into
+ * account the diff between each root flow emission.
+ *
+ * This is needed to minimize the number of [getDisplay] in this class. Note that if the
+ * [createValue] returns a null element, it will not be added in the output set.
+ */
+ private fun <T, V> Flow<Set<T>>.mapElementsLazily(createValue: (T) -> V?): Flow<Set<V>> {
+ var initialSet = emptySet<T>()
+ val currentMap = mutableMapOf<T, V>()
+ var resultSet = emptySet<V>()
+ return onEach { currentSet ->
+ val removed = initialSet - currentSet
+ val added = currentSet - initialSet
+ if (added.isNotEmpty() || removed.isNotEmpty()) {
+ added.forEach { key: T -> createValue(key)?.let { currentMap[key] = it } }
+ removed.forEach { key: T -> currentMap.remove(key) }
+ resultSet = currentMap.values.toSet() // Creates a **copy** of values
+ }
+ initialSet = currentSet
+ }
+ .map { resultSet }
+ }
+
private companion object {
const val TAG = "DisplayRepository"
val DEBUG = Log.isLoggable(TAG, Log.DEBUG) || Compile.IS_DEBUG
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
index f860893..3294c81 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
@@ -27,11 +27,13 @@
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.animation.Interpolators
import com.android.dream.lowlight.util.TruncatedInterpolator
+import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController
import com.android.systemui.complication.ComplicationHostViewController
import com.android.systemui.complication.ComplicationLayoutParams
import com.android.systemui.complication.ComplicationLayoutParams.POSITION_BOTTOM
import com.android.systemui.complication.ComplicationLayoutParams.POSITION_TOP
import com.android.systemui.complication.ComplicationLayoutParams.Position
+import com.android.systemui.dreams.dagger.DreamOverlayComponent.DreamOverlayScope
import com.android.systemui.dreams.dagger.DreamOverlayModule
import com.android.systemui.dreams.ui.viewmodel.DreamViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
@@ -45,12 +47,13 @@
import kotlinx.coroutines.launch
/** Controller for dream overlay animations. */
+@DreamOverlayScope
class DreamOverlayAnimationsController
@Inject
constructor(
private val mBlurUtils: BlurUtils,
private val mComplicationHostViewController: ComplicationHostViewController,
- private val mStatusBarViewController: DreamOverlayStatusBarViewController,
+ private val mStatusBarViewController: AmbientStatusBarViewController,
private val mOverlayStateController: DreamOverlayStateController,
@Named(DreamOverlayModule.DREAM_BLUR_RADIUS) private val mDreamBlurRadius: Int,
private val dreamViewModel: DreamViewModel,
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
index 1e725eb..245def8 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
@@ -40,6 +40,7 @@
import com.android.app.animation.Interpolators;
import com.android.dream.lowlight.LowLightTransitionCoordinator;
+import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController;
import com.android.systemui.ambient.touch.scrim.BouncerlessScrimController;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
@@ -55,6 +56,7 @@
import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.BlurUtils;
+import com.android.systemui.touch.TouchInsetManager;
import com.android.systemui.util.ViewController;
import kotlinx.coroutines.CoroutineDispatcher;
@@ -72,10 +74,12 @@
public class DreamOverlayContainerViewController extends
ViewController<DreamOverlayContainerView> implements
LowLightTransitionCoordinator.LowLightEnterListener {
- private final DreamOverlayStatusBarViewController mStatusBarViewController;
+ private final AmbientStatusBarViewController mStatusBarViewController;
+ private final TouchInsetManager.TouchInsetSession mTouchInsetSession;
private final BlurUtils mBlurUtils;
private final DreamOverlayAnimationsController mDreamOverlayAnimationsController;
private final DreamOverlayStateController mStateController;
+
private final LowLightTransitionCoordinator mLowLightTransitionCoordinator;
private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
private final ShadeInteractor mShadeInteractor;
@@ -188,8 +192,9 @@
ComplicationHostViewController complicationHostViewController,
@Named(DreamOverlayModule.DREAM_OVERLAY_CONTENT_VIEW) ViewGroup contentView,
@Named(DreamOverlayModule.HUB_GESTURE_INDICATOR_VIEW) View hubGestureIndicatorView,
- DreamOverlayStatusBarViewController statusBarViewController,
+ AmbientStatusBarViewController statusBarViewController,
LowLightTransitionCoordinator lowLightTransitionCoordinator,
+ TouchInsetManager.TouchInsetSession touchInsetSession,
BlurUtils blurUtils,
@Main Handler handler,
@Background CoroutineDispatcher backgroundDispatcher,
@@ -209,6 +214,7 @@
super(containerView);
mDreamOverlayContentView = contentView;
mStatusBarViewController = statusBarViewController;
+ mTouchInsetSession = touchInsetSession;
mBlurUtils = blurUtils;
mDreamOverlayAnimationsController = animationsController;
mStateController = stateController;
@@ -294,6 +300,7 @@
mHandler.removeCallbacksAndMessages(null);
mPrimaryBouncerCallbackInteractor.removeBouncerExpansionCallback(mBouncerExpansionCallback);
mBouncerlessScrimController.removeCallback(mBouncerlessExpansionCallback);
+ mTouchInsetSession.clear();
mDreamOverlayAnimationsController.cancelAnimations();
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayDotImageView.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayDotImageView.java
index 409b196..0833518 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayDotImageView.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayDotImageView.java
@@ -31,12 +31,13 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarView;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.AlphaOptimizedImageView;
/**
* An {@link AlphaOptimizedImageView} that is responsible for rendering a dot. Used by
- * {@link DreamOverlayStatusBarView}.
+ * {@link AmbientStatusBarView}.
*/
public class DreamOverlayDotImageView extends AlphaOptimizedImageView {
private final @ColorInt int mDotColor;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
index 789b7f8..76fcabd 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
@@ -25,9 +25,11 @@
import androidx.lifecycle.LifecycleOwner;
import com.android.internal.util.Preconditions;
+import com.android.systemui.ambient.statusbar.dagger.AmbientStatusBarComponent;
+import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarView;
+import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.DreamOverlayContainerView;
-import com.android.systemui.dreams.DreamOverlayStatusBarView;
import com.android.systemui.res.R;
import com.android.systemui.touch.TouchInsetManager;
@@ -60,7 +62,7 @@
public static DreamOverlayContainerView providesDreamOverlayContainerView(
LayoutInflater layoutInflater) {
return Preconditions.checkNotNull((DreamOverlayContainerView)
- layoutInflater.inflate(R.layout.dream_overlay_container, null),
+ layoutInflater.inflate(R.layout.dream_overlay_container, null),
"R.layout.dream_layout_container could not be properly inflated");
}
@@ -95,13 +97,23 @@
/** */
@Provides
@DreamOverlayComponent.DreamOverlayScope
- public static DreamOverlayStatusBarView providesDreamOverlayStatusBarView(
+ public static AmbientStatusBarView providesDreamOverlayStatusBarView(
DreamOverlayContainerView view) {
return Preconditions.checkNotNull(view.findViewById(R.id.dream_overlay_status_bar),
"R.id.status_bar must not be null");
}
- /** */
+ /**
+ * Provides the view controller for the {@link AmbientStatusBarView}
+ */
+ @Provides
+ @DreamOverlayComponent.DreamOverlayScope
+ public static AmbientStatusBarViewController providesStatusBarViewController(
+ AmbientStatusBarView view, AmbientStatusBarComponent.Factory factory) {
+ return factory.create(view).getController();
+ }
+
+ /** */
@Provides
@DreamOverlayComponent.DreamOverlayScope
@Named(MAX_BURN_IN_OFFSET)
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
index ea8d7d7..be4c903 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
@@ -17,21 +17,19 @@
package com.android.systemui.haptics.qs
import android.os.VibrationEffect
-import android.view.View
import androidx.annotation.VisibleForTesting
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.animation.Expandable
+import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.statusbar.VibratorHelper
+import com.android.systemui.statusbar.policy.KeyguardStateController
import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.combine
/**
* A class that handles the long press visuo-haptic effect for a QS tile.
*
- * The class is also a [View.OnTouchListener] to handle the touch events, clicks and long-press
- * gestures of the tile. The class also provides a [State] tha can be used to determine the current
- * state of the long press effect.
+ * The class can contain references to a [QSTile] and an [Expandable] to perform clicks and
+ * long-clicks on the tile. The class also provides a [State] tha can be used to determine the
+ * current state of the long press effect.
*
* @property[vibratorHelper] The [VibratorHelper] to deliver haptic effects.
* @property[effectDuration] The duration of the effect in ms.
@@ -41,7 +39,7 @@
@Inject
constructor(
private val vibratorHelper: VibratorHelper?,
- keyguardInteractor: KeyguardInteractor,
+ private val keyguardStateController: KeyguardStateController,
) {
var effectDuration = 0
@@ -51,19 +49,12 @@
var state = State.IDLE
private set
- /** Flow for view control and action */
- private val _postedActionType = MutableStateFlow<ActionType?>(null)
- val actionType: Flow<ActionType?> =
- combine(
- _postedActionType,
- keyguardInteractor.isKeyguardDismissible,
- ) { action, isDismissible ->
- if (!isDismissible && action == ActionType.LONG_PRESS) {
- ActionType.RESET_AND_LONG_PRESS
- } else {
- action
- }
- }
+ /** Callback object for effect actions */
+ var callback: Callback? = null
+
+ /** The [QSTile] and [Expandable] used to perform a long-click and click actions */
+ var qsTile: QSTile? = null
+ var expandable: Expandable? = null
/** Haptic effects */
private val durations =
@@ -106,33 +97,24 @@
State.IDLE -> {
setState(State.TIMEOUT_WAIT)
}
- State.RUNNING_BACKWARDS -> _postedActionType.value = ActionType.CANCEL_ANIMATOR
+ State.RUNNING_BACKWARDS -> callback?.onCancelAnimator()
else -> {}
}
}
fun handleActionUp() {
- when (state) {
- State.TIMEOUT_WAIT -> {
- _postedActionType.value = ActionType.CLICK
- setState(State.IDLE)
- }
- State.RUNNING_FORWARD -> {
- _postedActionType.value = ActionType.REVERSE_ANIMATOR
- setState(State.RUNNING_BACKWARDS)
- }
- else -> {}
+ if (state == State.RUNNING_FORWARD) {
+ setState(State.RUNNING_BACKWARDS)
+ callback?.onReverseAnimator()
}
}
fun handleActionCancel() {
when (state) {
- State.TIMEOUT_WAIT -> {
- setState(State.IDLE)
- }
+ State.TIMEOUT_WAIT -> setState(State.IDLE)
State.RUNNING_FORWARD -> {
- _postedActionType.value = ActionType.REVERSE_ANIMATOR
setState(State.RUNNING_BACKWARDS)
+ callback?.onReverseAnimator()
}
else -> {}
}
@@ -146,8 +128,15 @@
/** This function is called both when an animator completes or gets cancelled */
fun handleAnimationComplete() {
if (state == State.RUNNING_FORWARD) {
+ setState(State.IDLE)
vibrate(snapEffect)
- _postedActionType.value = ActionType.LONG_PRESS
+ if (keyguardStateController.isUnlocked) {
+ callback?.onPrepareForLaunch()
+ qsTile?.longClick(expandable)
+ } else {
+ callback?.onResetProperties()
+ qsTile?.longClick(expandable)
+ }
}
if (state != State.TIMEOUT_WAIT) {
// This will happen if the animator did not finish by being cancelled
@@ -161,12 +150,19 @@
fun handleTimeoutComplete() {
if (state == State.TIMEOUT_WAIT) {
- _postedActionType.value = ActionType.START_ANIMATOR
+ callback?.onStartAnimator()
}
}
- fun clearActionType() {
- _postedActionType.value = null
+ fun onTileClick(): Boolean {
+ if (state == State.TIMEOUT_WAIT) {
+ setState(State.IDLE)
+ qsTile?.let {
+ it.click(expandable)
+ return true
+ }
+ }
+ return false
}
/**
@@ -200,13 +196,22 @@
RUNNING_BACKWARDS, /* The effect was interrupted and is now running backwards */
}
- /* A type of action to perform on the view depending on the effect's state and logic */
- enum class ActionType {
- CLICK,
- LONG_PRESS,
- RESET_AND_LONG_PRESS,
- START_ANIMATOR,
- REVERSE_ANIMATOR,
- CANCEL_ANIMATOR,
+ /** Callbacks to notify view and animator actions */
+ interface Callback {
+
+ /** Prepare for an activity launch */
+ fun onPrepareForLaunch()
+
+ /** Reset the tile visual properties */
+ fun onResetProperties()
+
+ /** Start the effect animator */
+ fun onStartAnimator()
+
+ /** Reverse the effect animator */
+ fun onReverseAnimator()
+
+ /** Cancel the effect animator */
+ fun onCancelAnimator()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt
deleted file mode 100644
index 4875f48..0000000
--- a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.haptics.qs
-
-import android.animation.ValueAnimator
-import android.annotation.SuppressLint
-import android.view.MotionEvent
-import android.view.ViewConfiguration
-import android.view.animation.AccelerateDecelerateInterpolator
-import androidx.core.animation.doOnCancel
-import androidx.core.animation.doOnEnd
-import androidx.core.animation.doOnStart
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
-import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.qs.tileimpl.QSTileViewImpl
-import kotlinx.coroutines.DisposableHandle
-import kotlinx.coroutines.flow.filterNotNull
-
-object QSLongPressEffectViewBinder {
-
- fun bind(
- tile: QSTileViewImpl,
- qsLongPressEffect: QSLongPressEffect?,
- tileSpec: String?,
- ): DisposableHandle? {
- if (qsLongPressEffect == null) return null
-
- // Set the touch listener as the long-press effect
- setTouchListener(tile, qsLongPressEffect)
-
- return tile.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- // Action to perform
- launch({ "${tileSpec ?: "unknownTileSpec"}#LongPressEffect#action" }) {
- var effectAnimator: ValueAnimator? = null
-
- qsLongPressEffect.actionType.filterNotNull().collect { action ->
- when (action) {
- QSLongPressEffect.ActionType.CLICK -> {
- tile.performClick()
- qsLongPressEffect.clearActionType()
- }
- QSLongPressEffect.ActionType.LONG_PRESS -> {
- tile.prepareForLaunch()
- tile.performLongClick()
- qsLongPressEffect.clearActionType()
- }
- QSLongPressEffect.ActionType.RESET_AND_LONG_PRESS -> {
- tile.resetLongPressEffectProperties()
- tile.performLongClick()
- qsLongPressEffect.clearActionType()
- }
- QSLongPressEffect.ActionType.START_ANIMATOR -> {
- if (effectAnimator?.isRunning != true) {
- effectAnimator =
- ValueAnimator.ofFloat(0f, 1f).apply {
- this.duration =
- qsLongPressEffect.effectDuration.toLong()
- interpolator = AccelerateDecelerateInterpolator()
-
- doOnStart { qsLongPressEffect.handleAnimationStart() }
- addUpdateListener {
- val value = animatedValue as Float
- if (value == 0f) {
- tile.bringToFront()
- } else {
- tile.updateLongPressEffectProperties(value)
- }
- }
- doOnEnd { qsLongPressEffect.handleAnimationComplete() }
- doOnCancel { qsLongPressEffect.handleAnimationCancel() }
- start()
- }
- }
- }
- QSLongPressEffect.ActionType.REVERSE_ANIMATOR -> {
- effectAnimator?.let {
- val pausedProgress = it.animatedFraction
- qsLongPressEffect.playReverseHaptics(pausedProgress)
- it.reverse()
- }
- }
- QSLongPressEffect.ActionType.CANCEL_ANIMATOR -> {
- tile.resetLongPressEffectProperties()
- effectAnimator?.cancel()
- }
- }
- }
- }
- }
- }
- }
-
- @SuppressLint("ClickableViewAccessibility")
- private fun setTouchListener(tile: QSTileViewImpl, longPressEffect: QSLongPressEffect?) {
- tile.setOnTouchListener { _, event ->
- when (event.actionMasked) {
- MotionEvent.ACTION_DOWN -> {
- tile.postDelayed(
- { longPressEffect?.handleTimeoutComplete() },
- ViewConfiguration.getTapTimeout().toLong(),
- )
- longPressEffect?.handleActionDown()
- }
- MotionEvent.ACTION_UP -> longPressEffect?.handleActionUp()
- MotionEvent.ACTION_CANCEL -> longPressEffect?.handleActionCancel()
- }
- true
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardModule.kt b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardModule.kt
index fc9406b..c6fb4f9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardModule.kt
@@ -19,13 +19,12 @@
import com.android.systemui.keyboard.data.repository.KeyboardRepository
import com.android.systemui.keyboard.data.repository.KeyboardRepositoryImpl
-import com.android.systemui.keyboard.shortcut.ShortcutHelperModule
import com.android.systemui.keyboard.stickykeys.data.repository.StickyKeysRepository
import com.android.systemui.keyboard.stickykeys.data.repository.StickyKeysRepositoryImpl
import dagger.Binds
import dagger.Module
-@Module(includes = [ShortcutHelperModule::class])
+@Module
abstract class KeyboardModule {
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index f2a544e..209bc7a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -80,6 +80,7 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier;
import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindViewBinder;
import com.android.systemui.keyguard.ui.binder.WindowManagerLockscreenVisibilityViewBinder;
@@ -118,6 +119,7 @@
private final ShellTransitions mShellTransitions;
private final DisplayTracker mDisplayTracker;
private final PowerInteractor mPowerInteractor;
+ private final KeyguardInteractor mKeyguardInteractor;
private final Lazy<SceneInteractor> mSceneInteractorLazy;
private final Executor mMainExecutor;
@@ -338,6 +340,7 @@
WindowManagerOcclusionManager windowManagerOcclusionManager,
Lazy<SceneInteractor> sceneInteractorLazy,
@Main Executor mainExecutor,
+ KeyguardInteractor keyguardInteractor,
KeyguardEnabledInteractor keyguardEnabledInteractor) {
super();
mKeyguardViewMediator = keyguardViewMediator;
@@ -347,6 +350,7 @@
mDisplayTracker = displayTracker;
mFlags = featureFlags;
mPowerInteractor = powerInteractor;
+ mKeyguardInteractor = keyguardInteractor;
mSceneInteractorLazy = sceneInteractorLazy;
mMainExecutor = mainExecutor;
@@ -474,6 +478,7 @@
public void onDreamingStarted() {
trace("onDreamingStarted");
checkPermission();
+ mKeyguardInteractor.setDreaming(true);
mKeyguardViewMediator.onDreamingStarted();
}
@@ -481,6 +486,7 @@
public void onDreamingStopped() {
trace("onDreamingStopped");
checkPermission();
+ mKeyguardInteractor.setDreaming(false);
mKeyguardViewMediator.onDreamingStopped();
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index f3a1843..2d60fcc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -176,6 +176,8 @@
import com.android.systemui.wallpapers.data.repository.WallpaperRepository;
import com.android.wm.shell.keyguard.KeyguardTransitions;
+import dagger.Lazy;
+
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -185,7 +187,6 @@
import java.util.concurrent.Executor;
import java.util.function.Consumer;
-import dagger.Lazy;
import kotlinx.coroutines.CoroutineDispatcher;
/**
@@ -3014,7 +3015,7 @@
&& mPM.isInteractive(), WakeAndUnlockUpdateReason.HIDE);
}
- if ((mShowing && !mOccluded) || mUnlockingAndWakingFromDream) {
+ if (mBootCompleted && ((mShowing && !mOccluded) || mUnlockingAndWakingFromDream)) {
if (mUnlockingAndWakingFromDream) {
Log.d(TAG, "hiding keyguard before waking from dream");
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
index 1b342ed..180afb2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
@@ -142,15 +142,17 @@
nonApps: Array<RemoteAnimationTarget>,
finishedCallback: IRemoteAnimationFinishedCallback
) {
- if (apps.isNotEmpty()) {
- // Ensure that we've started a dismiss keyguard transition. WindowManager can start the
- // going away animation on its own, if an activity launches and then requests dismissing
- // the keyguard. In this case, this is the first and only signal we'll receive to start
- // a transition to GONE.
- keyguardTransitionInteractor.startDismissKeyguardTransition(
- reason = "Going away remote animation started"
- )
+ // Ensure that we've started a dismiss keyguard transition. WindowManager can start the
+ // going away animation on its own, if an activity launches and then requests dismissing the
+ // keyguard. In this case, this is the first and only signal we'll receive to start
+ // a transition to GONE. This transition needs to start even if we're not provided an app
+ // animation target - it's possible the app is destroyed on creation, etc. but we'll still
+ // be unlocking.
+ keyguardTransitionInteractor.startDismissKeyguardTransition(
+ reason = "Going away remote animation started"
+ )
+ if (apps.isNotEmpty()) {
goingAwayRemoteAnimationFinishedCallback = finishedCallback
keyguardSurfaceBehindAnimator.applyParamsToSurface(apps[0])
} else {
@@ -183,25 +185,11 @@
/**
* Sets the lockscreen state WM-side by calling ATMS#setLockScreenShown.
*
- * [lockscreenShowing] defaults to true, since it's only ever null during the boot sequence,
- * when we haven't yet called ATMS#setLockScreenShown. Typically,
- * setWmLockscreenState(lockscreenShowing = true) is called early in the boot sequence, before
- * setWmLockscreenState(aodVisible = true), so we don't expect to need to use this default, but
- * if so, true should be the right choice.
+ * If [lockscreenShowing] is null, it means we don't know if the lockscreen is showing yet. This
+ * will be decided by the [KeyguardTransitionBootInteractor] shortly.
*/
private fun setWmLockscreenState(
- lockscreenShowing: Boolean =
- this.isLockscreenShowing
- ?: true.also {
- Log.d(
- TAG,
- "Using isLockscreenShowing=true default in setWmLockscreenState, " +
- "because setAodVisible was called before the first " +
- "setLockscreenShown call during boot. This is not typical, but is " +
- "theoretically possible. If you're investigating the lockscreen " +
- "showing unexpectedly, start here."
- )
- },
+ lockscreenShowing: Boolean? = this.isLockscreenShowing,
aodVisible: Boolean = this.isAodVisible
) {
Log.d(
@@ -211,10 +199,27 @@
"aodVisible=$aodVisible)."
)
+ if (lockscreenShowing == null) {
+ Log.d(
+ TAG,
+ "isAodVisible=$aodVisible, but lockscreenShowing=null. Waiting for" +
+ "non-null lockscreenShowing before calling ATMS#setLockScreenShown, which" +
+ "will happen once KeyguardTransitionBootInteractor starts the boot transition."
+ )
+ this.isAodVisible = aodVisible
+ return
+ }
+
if (this.isLockscreenShowing == lockscreenShowing && this.isAodVisible == aodVisible) {
return
}
+ Log.d(
+ TAG,
+ "ATMS#setLockScreenShown(" +
+ "isLockscreenShowing=$lockscreenShowing, " +
+ "aodVisible=$aodVisible)."
+ )
activityTaskManagerService.setLockScreenShown(lockscreenShowing, aodVisible)
this.isLockscreenShowing = lockscreenShowing
this.isAodVisible = aodVisible
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index f15896b..d508b2b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -148,7 +148,7 @@
* Dozing/AOD is a specific type of dream, but it is also possible for other non-systemui dreams
* to be active, such as screensavers.
*/
- val isDreaming: Flow<Boolean>
+ val isDreaming: MutableStateFlow<Boolean>
/** Observable for whether the device is dreaming with an overlay, see [DreamOverlayService] */
val isDreamingWithOverlay: Flow<Boolean>
@@ -250,6 +250,9 @@
/** Sets the current amount of alpha that should be used for rendering the keyguard. */
fun setKeyguardAlpha(alpha: Float)
+ /** Whether the device is actively dreaming */
+ fun setDreaming(isDreaming: Boolean)
+
/**
* Returns whether the keyguard bottom area should be constrained to the top of the lock icon
*/
@@ -509,25 +512,7 @@
}
.distinctUntilChanged()
- override val isDreaming: Flow<Boolean> =
- conflatedCallbackFlow {
- val callback =
- object : KeyguardUpdateMonitorCallback() {
- override fun onDreamingStateChanged(isDreaming: Boolean) {
- trySendWithFailureLogging(isDreaming, TAG, "updated isDreaming")
- }
- }
- keyguardUpdateMonitor.registerCallback(callback)
- trySendWithFailureLogging(
- keyguardUpdateMonitor.isDreaming,
- TAG,
- "initial isDreaming",
- )
-
- awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
- }
- .flowOn(mainDispatcher)
- .distinctUntilChanged()
+ override val isDreaming: MutableStateFlow<Boolean> = MutableStateFlow(false)
override val linearDozeAmount: Flow<Float> = conflatedCallbackFlow {
val callback =
@@ -674,6 +659,10 @@
_keyguardAlpha.value = alpha
}
+ override fun setDreaming(isDreaming: Boolean) {
+ this.isDreaming.value = isDreaming
+ }
+
override fun isUdfpsSupported(): Boolean = keyguardUpdateMonitor.isUdfpsSupported
override fun setQuickSettingsVisible(isVisible: Boolean) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
index 8ec460a..1b201ce 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
@@ -20,6 +20,7 @@
import android.animation.ValueAnimator
import android.animation.ValueAnimator.AnimatorUpdateListener
import android.annotation.FloatRange
+import android.annotation.SuppressLint
import android.os.Trace
import android.util.Log
import com.android.app.tracing.coroutines.withContext
@@ -117,10 +118,11 @@
constructor(
@Main val mainDispatcher: CoroutineDispatcher,
) : KeyguardTransitionRepository {
- /*
- * Each transition between [KeyguardState]s will have an associated Flow.
- * In order to collect these events, clients should call [transition].
+ /**
+ * Each transition between [KeyguardState]s will have an associated Flow. In order to collect
+ * these events, clients should call [transition].
*/
+ @SuppressLint("SharedFlowCreation")
private val _transitions =
MutableSharedFlow<TransitionStep>(
replay = 2,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
index 756c6c2..49d00af 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
@@ -24,9 +24,12 @@
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.KeyguardWmStateRefactor
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
import com.android.wm.shell.animation.Interpolators
import javax.inject.Inject
@@ -36,7 +39,6 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filter
@@ -81,17 +83,16 @@
}
val surfaceBehindVisibility: Flow<Boolean?> =
- combine(
- transitionInteractor.startedKeyguardTransitionStep,
- transitionInteractor.transitionStepsFromState(KeyguardState.ALTERNATE_BOUNCER)
- ) { startedStep, fromBouncerStep ->
- if (startedStep.to != KeyguardState.GONE) {
- return@combine null
- }
-
+ transitionInteractor
+ .transition(
+ edge = Edge.create(from = KeyguardState.ALTERNATE_BOUNCER, to = Scenes.Gone),
+ edgeWithoutSceneContainer =
+ Edge.create(from = KeyguardState.ALTERNATE_BOUNCER, to = KeyguardState.GONE)
+ )
+ .map<TransitionStep, Boolean?> {
// The alt bouncer is pretty fast to hide, so start the surface behind animation
// around 30%.
- fromBouncerStep.value > 0.3f
+ it.value > 0.3f
}
.onStart {
// Default to null ("don't care, use a reasonable default").
@@ -113,13 +114,26 @@
powerInteractor.isAwake,
keyguardInteractor.isAodAvailable,
communalInteractor.isIdleOnCommunal,
+ communalInteractor.editModeOpen,
keyguardInteractor.isKeyguardOccluded,
)
.filterRelevantKeyguardStateAnd {
(isAlternateBouncerShowing, isPrimaryBouncerShowing, _, _, _) ->
!isAlternateBouncerShowing && !isPrimaryBouncerShowing
}
- .collect { (_, _, isAwake, isAodAvailable, isIdleOnCommunal, isOccluded) ->
+ .collect {
+ (
+ _,
+ _,
+ isAwake,
+ isAodAvailable,
+ isIdleOnCommunal,
+ isCommunalEditMode,
+ isOccluded) ->
+ // When unlocking over glanceable hub to enter edit mode, transitioning directly
+ // to GONE prevents the lockscreen flash. Let listenForAlternateBouncerToGone
+ // handle it.
+ if (isCommunalEditMode) return@collect
val to =
if (!isAwake) {
if (isAodAvailable) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
index f5e98f1..f3692bd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
@@ -29,6 +29,7 @@
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.util.kotlin.Utils.Companion.sample
+import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineDispatcher
@@ -65,6 +66,7 @@
override fun start() {
listenForDozingToAny()
+ listenForDozingToGoneViaBiometrics()
listenForWakeFromDozing()
listenForTransitionToCamera(scope, keyguardInteractor)
}
@@ -77,6 +79,35 @@
isKeyguardDismissible && !isKeyguardShowing
}
+ private fun listenForDozingToGoneViaBiometrics() {
+ if (KeyguardWmStateRefactor.isEnabled) {
+ return
+ }
+
+ // This is separate from `listenForDozingToAny` because any delay on wake and unlock will
+ // cause a noticeable issue with animations
+ scope.launch {
+ powerInteractor.isAwake
+ .filterRelevantKeyguardStateAnd { isAwake -> isAwake }
+ .sample(
+ keyguardInteractor.biometricUnlockState,
+ ::Pair,
+ )
+ .collect {
+ (
+ _,
+ biometricUnlockState,
+ ) ->
+ if (isWakeAndUnlock(biometricUnlockState.mode)) {
+ startTransitionTo(
+ KeyguardState.GONE,
+ ownerReason = "biometric wake and unlock",
+ )
+ }
+ }
+ }
+ }
+
private fun listenForDozingToAny() {
if (KeyguardWmStateRefactor.isEnabled) {
return
@@ -87,7 +118,6 @@
.debounce(50L)
.filterRelevantKeyguardStateAnd { isAwake -> isAwake }
.sample(
- keyguardInteractor.biometricUnlockState,
keyguardInteractor.isKeyguardOccluded,
communalInteractor.isIdleOnCommunal,
canTransitionToGoneOnWake,
@@ -96,7 +126,6 @@
.collect {
(
_,
- biometricUnlockState,
occluded,
isIdleOnCommunal,
canTransitionToGoneOnWake,
@@ -104,8 +133,6 @@
startTransitionTo(
if (!deviceEntryRepository.isLockscreenEnabled()) {
KeyguardState.GONE
- } else if (isWakeAndUnlock(biometricUnlockState.mode)) {
- KeyguardState.GONE
} else if (canTransitionToGoneOnWake) {
KeyguardState.GONE
} else if (primaryBouncerShowing) {
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 8cab3cd..f30eef0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -24,16 +24,18 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.flags.FeatureFlags
import com.android.systemui.keyguard.KeyguardWmStateRefactor
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.StatusBarState.KEYGUARD
import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
import java.util.UUID
@@ -59,7 +61,6 @@
@Background bgDispatcher: CoroutineDispatcher,
@Main mainDispatcher: CoroutineDispatcher,
keyguardInteractor: KeyguardInteractor,
- private val flags: FeatureFlags,
private val shadeRepository: ShadeRepository,
powerInteractor: PowerInteractor,
private val glanceableHubTransitions: GlanceableHubTransitions,
@@ -97,14 +98,13 @@
* LOCKSCREEN is running.
*/
val surfaceBehindVisibility: Flow<Boolean?> =
- transitionInteractor.startedKeyguardTransitionStep
- .map { startedStep ->
- if (startedStep.to != KeyguardState.GONE) {
- // LOCKSCREEN to anything but GONE does not require any special surface
- // visibility handling.
- return@map null
- }
-
+ transitionInteractor
+ .transition(
+ edge = Edge.create(from = KeyguardState.LOCKSCREEN, to = Scenes.Gone),
+ edgeWithoutSceneContainer =
+ Edge.create(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GONE)
+ )
+ .map<TransitionStep, Boolean?> {
true // Make the surface visible during LS -> GONE transitions.
}
.onStart {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index 8cf4b53..f8208b3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -22,11 +22,12 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.flags.FeatureFlags
import com.android.systemui.keyguard.KeyguardWmStateRefactor
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
+import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
@@ -38,8 +39,9 @@
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
@@ -54,7 +56,6 @@
@Main mainDispatcher: CoroutineDispatcher,
keyguardInteractor: KeyguardInteractor,
private val communalInteractor: CommunalInteractor,
- private val flags: FeatureFlags,
private val keyguardSecurityModel: KeyguardSecurityModel,
private val selectedUserInteractor: SelectedUserInteractor,
powerInteractor: PowerInteractor,
@@ -79,21 +80,25 @@
}
val surfaceBehindVisibility: Flow<Boolean?> =
- combine(
- transitionInteractor.startedKeyguardTransitionStep,
- transitionInteractor.transitionStepsFromState(KeyguardState.PRIMARY_BOUNCER)
- ) { startedStep, fromBouncerStep ->
- if (startedStep.to != KeyguardState.GONE) {
- return@combine null
+ if (SceneContainerFlag.isEnabled) {
+ // The edge Scenes.Bouncer <-> Scenes.Gone is handled by STL
+ flowOf(null)
+ } else {
+ transitionInteractor
+ .transition(
+ edge = Edge.INVALID,
+ edgeWithoutSceneContainer =
+ Edge.create(from = KeyguardState.PRIMARY_BOUNCER, to = KeyguardState.GONE)
+ )
+ .map<TransitionStep, Boolean?> {
+ it.value > TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD
}
-
- fromBouncerStep.value > TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD
- }
- .onStart {
- // Default to null ("don't care, use a reasonable default").
- emit(null)
- }
- .distinctUntilChanged()
+ .onStart {
+ // Default to null ("don't care, use a reasonable default").
+ emit(null)
+ }
+ .distinctUntilChanged()
+ }
fun dismissPrimaryBouncer() {
scope.launch { startTransitionTo(KeyguardState.GONE) }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 73835a3c..ef96be0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -51,6 +51,7 @@
import com.android.systemui.statusbar.notification.NotificationUtils.interpolate
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
+import com.android.systemui.util.kotlin.Utils.Companion.sampleFilter
import com.android.systemui.util.kotlin.pairwise
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
@@ -77,6 +78,7 @@
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.transform
/**
* Encapsulates business-logic related to the keyguard but not to a more specific part within it.
@@ -91,7 +93,7 @@
bouncerRepository: KeyguardBouncerRepository,
configurationInteractor: ConfigurationInteractor,
shadeRepository: ShadeRepository,
- keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
sceneInteractorProvider: Provider<SceneInteractor>,
private val fromGoneTransitionInteractor: Provider<FromGoneTransitionInteractor>,
private val fromLockscreenTransitionInteractor: Provider<FromLockscreenTransitionInteractor>,
@@ -110,7 +112,7 @@
keyguardTransitionInteractor.transitionState.map { step ->
val startingProgress = lastChangeStep.value
val progress = step.value
- if (step.to == AOD && progress >= startingProgress) {
+ if (step.to == AOD && progress >= startingProgress && startingProgress < 1f) {
val adjustedProgress =
((progress - startingProgress) / (1F - startingProgress)).coerceIn(
0F,
@@ -248,21 +250,17 @@
val isKeyguardGoingAway: Flow<Boolean> = repository.isKeyguardGoingAway
/** Keyguard can be clipped at the top as the shade is dragged */
- val topClippingBounds: Flow<Int?> =
- combineTransform(
- configurationInteractor.onAnyConfigurationChange,
+ val topClippingBounds: Flow<Int?> by lazy {
+ repository.topClippingBounds
+ .sampleFilter(
keyguardTransitionInteractor
- .transitionValue(GONE)
- .map { it == 1f }
- .onStart { emit(false) }
- .distinctUntilChanged(),
- repository.topClippingBounds
- ) { _, isGone, topClippingBounds ->
- if (!isGone) {
- emit(topClippingBounds)
- }
+ .transitionValue(scene = Scenes.Gone, stateWithoutSceneContainer = GONE)
+ .onStart { emit(0f) }
+ ) { goneValue ->
+ goneValue != 1f
}
.distinctUntilChanged()
+ }
/** Last point that [KeyguardRootView] view was tapped */
val lastRootViewTapPosition: Flow<Point?> = repository.lastRootViewTapPosition.asStateFlow()
@@ -330,28 +328,35 @@
* This uses legacyShadeExpansion to process swipe up events. In the future, the touch input
* signal should be sent directly to transitions.
*/
- val dismissAlpha: Flow<Float?> =
+ val dismissAlpha: Flow<Float> =
shadeRepository.legacyShadeExpansion
- .filter { it < 1f }
.sampleCombine(
statusBarState,
keyguardTransitionInteractor.currentKeyguardState,
+ keyguardTransitionInteractor.transitionState,
isKeyguardDismissible,
)
- .map {
- (legacyShadeExpansion, statusBarState, currentKeyguardState, isKeyguardDismissible)
- ->
+ .filter { (_, _, _, step, _) -> !step.transitionState.isTransitioning() }
+ .transform {
+ (
+ legacyShadeExpansion,
+ statusBarState,
+ currentKeyguardState,
+ step,
+ isKeyguardDismissible) ->
if (
statusBarState == StatusBarState.KEYGUARD &&
isKeyguardDismissible &&
- currentKeyguardState == LOCKSCREEN
+ currentKeyguardState == LOCKSCREEN &&
+ legacyShadeExpansion != 1f
) {
- MathUtils.constrainedMap(0f, 1f, 0.95f, 1f, legacyShadeExpansion)
- } else {
- null
+ emit(MathUtils.constrainedMap(0f, 1f, 0.95f, 1f, legacyShadeExpansion))
+ } else if (legacyShadeExpansion == 0f || legacyShadeExpansion == 1f) {
+ // Resets alpha state
+ emit(1f)
}
}
- .onStart { emit(null) }
+ .onStart { emit(1f) }
.distinctUntilChanged()
val keyguardTranslationY: Flow<Float> =
@@ -477,6 +482,10 @@
repository.topClippingBounds.value = top
}
+ fun setDreaming(isDreaming: Boolean) {
+ repository.setDreaming(isDreaming)
+ }
+
/** Temporary shim, until [KeyguardWmStateRefactor] is enabled */
fun showKeyguard() {
fromGoneTransitionInteractor.get().showKeyguard()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index 2766b71..37272dc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -223,6 +223,17 @@
}
}
+ fun transitionValue(
+ scene: SceneKey,
+ stateWithoutSceneContainer: KeyguardState,
+ ): Flow<Float> {
+ return if (SceneContainerFlag.isEnabled) {
+ sceneInteractor.get().transitionProgress(scene)
+ } else {
+ transitionValue(stateWithoutSceneContainer)
+ }
+ }
+
/**
* The amount of transition into or out of the given [KeyguardState].
*
@@ -232,26 +243,13 @@
fun transitionValue(
state: KeyguardState,
): Flow<Float> {
+ if (SceneContainerFlag.isEnabled && state != state.mapToSceneContainerState()) {
+ Log.e(TAG, "SceneContainer is enabled but a deprecated state $state is used.")
+ return transitionValue(state.mapToSceneContainerScene()!!, state)
+ }
return getTransitionValueFlow(state)
}
- /**
- * AOD<->* transition information, mapped to dozeAmount range of AOD (1f) <->
- * * (0f).
- */
- @SuppressLint("SharedFlowCreation")
- val dozeAmountTransition: Flow<TransitionStep> =
- repository.transitions
- .filter { step -> step.from == AOD || step.to == AOD }
- .map { step ->
- if (step.from == AOD) {
- step.copy(value = 1 - step.value)
- } else {
- step
- }
- }
- .shareIn(scope, SharingStarted.Eagerly, replay = 1)
-
/** The last [TransitionStep] with a [TransitionState] of STARTED */
val startedKeyguardTransitionStep: Flow<TransitionStep> =
repository.transitions.filter { step -> step.transitionState == TransitionState.STARTED }
@@ -267,8 +265,6 @@
.map { step -> step.to }
.shareIn(scope, SharingStarted.Eagerly, replay = 1)
- val currentTransitionInfo: StateFlow<TransitionInfo> = repository.currentTransitionInfoInternal
-
/** The from state of the last [TransitionState.STARTED] transition. */
// TODO: is it performant to have several SharedFlows side by side instead of one?
@SuppressLint("SharedFlowCreation")
@@ -415,14 +411,6 @@
/** Whether we've currently STARTED a transition and haven't yet FINISHED it. */
val isInTransitionToAnyState = isInTransitionWhere({ true }, { true })
- fun transitionStepsFromState(fromState: KeyguardState): Flow<TransitionStep> {
- return transition(Edge.create(from = fromState, to = null))
- }
-
- fun transitionStepsToState(toState: KeyguardState): Flow<TransitionStep> {
- return transition(Edge.create(from = null, to = toState))
- }
-
/**
* Called to start a transition that will ultimately dismiss the keyguard from the current
* state.
@@ -558,10 +546,6 @@
return currentKeyguardState.replayCache.last()
}
- fun getStartedState(): KeyguardState {
- return startedKeyguardState.replayCache.last()
- }
-
fun getStartedFromState(): KeyguardState {
return startedKeyguardFromState.replayCache.last()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index 88e6602..069f65b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -105,7 +105,17 @@
is ObservableTransitionState.Transition ->
when {
transitionState.fromScene == Scenes.Lockscreen &&
- transitionState.toScene == Scenes.Gone -> flowOf(true)
+ transitionState.toScene == Scenes.Gone ->
+ sceneInteractor
+ .get()
+ .isTransitionUserInputOngoing
+ .flatMapLatestConflated { isUserInputOngoing ->
+ if (isUserInputOngoing) {
+ isDeviceEntered
+ } else {
+ flowOf(true)
+ }
+ }
transitionState.fromScene == Scenes.Bouncer &&
transitionState.toScene == Scenes.Gone ->
transitionState.progress.map { progress ->
@@ -229,11 +239,14 @@
val aodVisibility: Flow<Boolean> =
combine(
keyguardInteractor.isDozing,
+ keyguardInteractor.isAodAvailable,
keyguardInteractor.biometricUnlockState,
- ) { isDozing, biometricUnlockState ->
+ ) { isDozing, isAodAvailable, biometricUnlockState ->
// AOD is visible if we're dozing, unless we are wake and unlocking (where we go
// directly from AOD to unlocked while dozing).
- isDozing && !BiometricUnlockMode.isWakeAndUnlock(biometricUnlockState.mode)
+ isDozing &&
+ isAodAvailable &&
+ !BiometricUnlockMode.isWakeAndUnlock(biometricUnlockState.mode)
}
.distinctUntilChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
index 3baeb76..9b3ba7d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
@@ -131,7 +131,7 @@
val newTransition =
TransitionInfo(
ownerName = this::class.java.simpleName,
- from = transitionInteractor.currentTransitionInfo.value.to,
+ from = transitionInteractor.currentTransitionInfoInternal.value.to,
to = state,
animator = null,
modeOnCanceled = TransitionModeOnCanceled.REVERSE
@@ -150,7 +150,7 @@
private suspend fun handleTransition(transition: ObservableTransitionState.Transition) {
if (transition.fromScene == Scenes.Lockscreen) {
if (currentTransitionId != null) {
- val currentToState = transitionInteractor.currentTransitionInfo.value.to
+ val currentToState = transitionInteractor.currentTransitionInfoInternal.value.to
if (currentToState == UNDEFINED) {
transitionKtfTo(transitionInteractor.getStartedFromState())
}
@@ -201,7 +201,7 @@
}
private suspend fun startTransitionFromLockscreen() {
- val currentState = transitionInteractor.currentTransitionInfo.value.to
+ val currentState = transitionInteractor.currentTransitionInfoInternal.value.to
val newTransition =
TransitionInfo(
ownerName = this::class.java.simpleName,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt
index c1e8d22..1306b26 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt
@@ -96,10 +96,23 @@
companion object {
private const val TAG = "Edge"
+ @JvmStatic
+ @JvmOverloads
fun create(from: KeyguardState? = null, to: KeyguardState? = null) = StateToState(from, to)
+ @JvmStatic
+ @JvmOverloads
fun create(from: KeyguardState? = null, to: SceneKey) = StateToScene(from, to)
+ @JvmStatic
+ @JvmOverloads
fun create(from: SceneKey, to: KeyguardState? = null) = SceneToState(from, to)
+
+ /**
+ * This edge is a placeholder for when an edge needs to be passed but there is no edge for
+ * this flag configuration available. Usually for Scene <-> Scene edges with scene container
+ * enabled where these edges are managed by STL separately.
+ */
+ val INVALID = StateToState(UNDEFINED, UNDEFINED)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt
index 2b4c4af..0a8c190 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt
@@ -15,6 +15,9 @@
*/
package com.android.systemui.keyguard.shared.model
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
+
/** This information will flow from the [KeyguardTransitionRepository] to control the UI layer */
data class TransitionStep
@JvmOverloads
@@ -39,3 +42,6 @@
return to == state && transitionState == TransitionState.FINISHED
}
}
+
+fun Flow<TransitionStep>.filterState(transitionState: TransitionState) =
+ this.filter { it.transitionState == transitionState }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt
index 39f1ebe..aa0a9d9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt
@@ -62,7 +62,7 @@
addTransition(
clockViewModel.currentClock.value?.let { DefaultClockSteppingTransition(it) }
)
- else -> addTransition(ClockSizeTransition(config, clockViewModel, smartspaceViewModel))
+ else -> addTransition(ClockSizeTransition(config, clockViewModel))
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
index f17dbd2..4d914c7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
@@ -31,8 +31,9 @@
import com.android.app.animation.Interpolators
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Type
+import com.android.systemui.keyguard.ui.view.layout.sections.transitions.ClockSizeTransition.SmartspaceMoveTransition.Companion.STATUS_AREA_MOVE_DOWN_MILLIS
+import com.android.systemui.keyguard.ui.view.layout.sections.transitions.ClockSizeTransition.SmartspaceMoveTransition.Companion.STATUS_AREA_MOVE_UP_MILLIS
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
import com.android.systemui.res.R
import com.android.systemui.shared.R as sharedR
import com.google.android.material.math.MathUtils
@@ -46,13 +47,12 @@
class ClockSizeTransition(
config: IntraBlueprintTransition.Config,
clockViewModel: KeyguardClockViewModel,
- smartspaceViewModel: KeyguardSmartspaceViewModel,
) : TransitionSet() {
init {
ordering = ORDERING_TOGETHER
if (config.type != Type.SmartspaceVisibility) {
- addTransition(ClockFaceOutTransition(config, clockViewModel, smartspaceViewModel))
- addTransition(ClockFaceInTransition(config, clockViewModel, smartspaceViewModel))
+ addTransition(ClockFaceOutTransition(config, clockViewModel))
+ addTransition(ClockFaceInTransition(config, clockViewModel))
}
addTransition(SmartspaceMoveTransition(config, clockViewModel))
}
@@ -60,8 +60,11 @@
abstract class VisibilityBoundsTransition() : Transition() {
abstract val captureSmartspace: Boolean
protected val TAG = this::class.simpleName!!
+
override fun captureEndValues(transition: TransitionValues) = captureValues(transition)
+
override fun captureStartValues(transition: TransitionValues) = captureValues(transition)
+
override fun getTransitionProperties(): Array<String> = TRANSITION_PROPERTIES
private fun captureValues(transition: TransitionValues) {
@@ -222,21 +225,19 @@
}
}
- class ClockFaceInTransition(
+ abstract class ClockFaceTransition(
config: IntraBlueprintTransition.Config,
val viewModel: KeyguardClockViewModel,
- val smartspaceViewModel: KeyguardSmartspaceViewModel,
) : VisibilityBoundsTransition() {
- override val captureSmartspace = !viewModel.isLargeClockVisible.value
+ protected abstract val isLargeClock: Boolean
+ protected abstract val smallClockMoveScale: Float
+ override val captureSmartspace
+ get() = !isLargeClock
- init {
- duration = CLOCK_IN_MILLIS
- startDelay = CLOCK_IN_START_DELAY_MILLIS
- interpolator = CLOCK_IN_INTERPOLATOR
-
- if (viewModel.isLargeClockVisible.value) {
+ protected fun addTargets() {
+ if (isLargeClock) {
viewModel.currentClock.value?.let {
- if (DEBUG) Log.i(TAG, "Large Clock In: ${it.largeClock.layout.views}")
+ if (DEBUG) Log.i(TAG, "Adding large clock views: ${it.largeClock.layout.views}")
it.largeClock.layout.views.forEach { addTarget(it) }
}
?: run {
@@ -244,7 +245,7 @@
addTarget(R.id.lockscreen_clock_view_large)
}
} else {
- if (DEBUG) Log.i(TAG, "Small Clock In")
+ if (DEBUG) Log.i(TAG, "Adding small clock")
addTarget(R.id.lockscreen_clock_view)
}
}
@@ -262,89 +263,59 @@
if (fromIsVis == toIsVis) return
fromBounds.set(toBounds)
- if (viewModel.isLargeClockVisible.value) {
+ if (isLargeClock) {
// Large clock shouldn't move; fromBounds already set
} else if (toSSBounds != null && fromSSBounds != null) {
// Instead of moving the small clock the full distance, we compute the distance
// smartspace will move. We then scale this to match the duration of this animation
// so that the small clock moves at the same speed as smartspace.
val ssTranslation =
- abs((toSSBounds.top - fromSSBounds.top) * SMALL_CLOCK_IN_MOVE_SCALE).toInt()
+ abs((toSSBounds.top - fromSSBounds.top) * smallClockMoveScale).toInt()
fromBounds.top = toBounds.top - ssTranslation
fromBounds.bottom = toBounds.bottom - ssTranslation
} else {
Log.e(TAG, "mutateBounds: smallClock received no smartspace bounds")
}
}
+ }
+
+ class ClockFaceInTransition(
+ config: IntraBlueprintTransition.Config,
+ viewModel: KeyguardClockViewModel,
+ ) : ClockFaceTransition(config, viewModel) {
+ override val isLargeClock = viewModel.isLargeClockVisible.value
+ override val smallClockMoveScale = CLOCK_IN_MILLIS / STATUS_AREA_MOVE_DOWN_MILLIS.toFloat()
+
+ init {
+ duration = CLOCK_IN_MILLIS
+ startDelay = CLOCK_IN_START_DELAY_MILLIS
+ interpolator = CLOCK_IN_INTERPOLATOR
+ addTargets()
+ }
companion object {
const val CLOCK_IN_MILLIS = 167L
const val CLOCK_IN_START_DELAY_MILLIS = 133L
val CLOCK_IN_INTERPOLATOR = Interpolators.LINEAR_OUT_SLOW_IN
- const val SMALL_CLOCK_IN_MOVE_SCALE =
- CLOCK_IN_MILLIS / SmartspaceMoveTransition.STATUS_AREA_MOVE_DOWN_MILLIS.toFloat()
}
}
class ClockFaceOutTransition(
config: IntraBlueprintTransition.Config,
- val viewModel: KeyguardClockViewModel,
- val smartspaceViewModel: KeyguardSmartspaceViewModel,
- ) : VisibilityBoundsTransition() {
- override val captureSmartspace = viewModel.isLargeClockVisible.value
+ viewModel: KeyguardClockViewModel,
+ ) : ClockFaceTransition(config, viewModel) {
+ override val isLargeClock = !viewModel.isLargeClockVisible.value
+ override val smallClockMoveScale = CLOCK_OUT_MILLIS / STATUS_AREA_MOVE_UP_MILLIS.toFloat()
init {
duration = CLOCK_OUT_MILLIS
interpolator = CLOCK_OUT_INTERPOLATOR
-
- if (viewModel.isLargeClockVisible.value) {
- if (DEBUG) Log.i(TAG, "Small Clock Out")
- addTarget(R.id.lockscreen_clock_view)
- } else {
- viewModel.currentClock.value?.let {
- if (DEBUG) Log.i(TAG, "Large Clock Out: ${it.largeClock.layout.views}")
- it.largeClock.layout.views.forEach { addTarget(it) }
- }
- ?: run {
- Log.e(TAG, "No large clock set, falling back")
- addTarget(R.id.lockscreen_clock_view_large)
- }
- }
- }
-
- override fun mutateBounds(
- view: View,
- fromIsVis: Boolean,
- toIsVis: Boolean,
- fromBounds: Rect,
- toBounds: Rect,
- fromSSBounds: Rect?,
- toSSBounds: Rect?
- ) {
- // Move normally if clock is not changing visibility
- if (fromIsVis == toIsVis) return
-
- toBounds.set(fromBounds)
- if (!viewModel.isLargeClockVisible.value) {
- // Large clock shouldn't move; toBounds already set
- } else if (toSSBounds != null && fromSSBounds != null) {
- // Instead of moving the small clock the full distance, we compute the distance
- // smartspace will move. We then scale this to match the duration of this animation
- // so that the small clock moves at the same speed as smartspace.
- val ssTranslation =
- abs((toSSBounds.top - fromSSBounds.top) * SMALL_CLOCK_OUT_MOVE_SCALE).toInt()
- toBounds.top = fromBounds.top - ssTranslation
- toBounds.bottom = fromBounds.bottom - ssTranslation
- } else {
- Log.e(TAG, "mutateBounds: smallClock received no smartspace bounds")
- }
+ addTargets()
}
companion object {
const val CLOCK_OUT_MILLIS = 133L
val CLOCK_OUT_INTERPOLATOR = Interpolators.LINEAR
- const val SMALL_CLOCK_OUT_MOVE_SCALE =
- CLOCK_OUT_MILLIS / SmartspaceMoveTransition.STATUS_AREA_MOVE_UP_MILLIS.toFloat()
}
}
@@ -353,15 +324,14 @@
val config: IntraBlueprintTransition.Config,
viewModel: KeyguardClockViewModel,
) : VisibilityBoundsTransition() {
+ private val isLargeClock = viewModel.isLargeClockVisible.value
override val captureSmartspace = false
init {
duration =
- if (viewModel.isLargeClockVisible.value) STATUS_AREA_MOVE_UP_MILLIS
- else STATUS_AREA_MOVE_DOWN_MILLIS
+ if (isLargeClock) STATUS_AREA_MOVE_UP_MILLIS else STATUS_AREA_MOVE_DOWN_MILLIS
interpolator = Interpolators.EMPHASIZED
addTarget(sharedR.id.date_smartspace_view)
- addTarget(sharedR.id.weather_smartspace_view)
addTarget(sharedR.id.bc_smartspace_view)
// Notifications normally and media on split shade needs to be moved
@@ -391,6 +361,6 @@
}
companion object {
- val DEBUG = true
+ val DEBUG = false
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
index c05a1b7..d9a6d64 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
@@ -29,6 +29,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.BurnInModel
import com.android.systemui.keyguard.shared.model.ClockSize
+import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.StateToValue
import com.android.systemui.res.R
import javax.inject.Inject
@@ -111,8 +112,8 @@
params: BurnInParameters,
): Flow<BurnInModel> {
return combine(
- keyguardTransitionInteractor.dozeAmountTransition.map {
- Interpolators.FAST_OUT_SLOW_IN.getInterpolation(it.value)
+ keyguardTransitionInteractor.transitionValue(KeyguardState.AOD).map {
+ Interpolators.FAST_OUT_SLOW_IN.getInterpolation(it)
},
burnInInteractor.burnIn(
xDimenResourceId = R.dimen.burn_in_prevention_offset_x,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
index 0f1f5c1..06b76b3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
@@ -52,8 +52,11 @@
udfpsOverlayInteractor: UdfpsOverlayInteractor,
) {
private val isShowingAodOrDozing: Flow<Boolean> =
- transitionInteractor.startedKeyguardState.map { keyguardState ->
- keyguardState == KeyguardState.AOD || keyguardState == KeyguardState.DOZING
+ combine(
+ transitionInteractor.startedKeyguardState,
+ transitionInteractor.transitionValue(KeyguardState.DOZING),
+ ) { startedKeyguardState, dozingTransitionValue ->
+ startedKeyguardState == KeyguardState.AOD || dozingTransitionValue == 1f
}
private fun getColor(usingBackgroundProtection: Boolean): Int {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
index fa43ec2..92bba38 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
@@ -264,7 +264,7 @@
accessibilityInteractor.isEnabled.flatMapLatest { touchExplorationEnabled ->
if (touchExplorationEnabled) {
combine(iconType, isInteractive) { iconType, isInteractive ->
- if (isInteractive) {
+ if (isInteractive || iconType == DeviceEntryIconView.IconType.LOCK) {
iconType.toAccessibilityHintType()
} else {
DeviceEntryIconView.AccessibilityHintType.NONE
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt
index 77ebfce..480f948 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt
@@ -16,7 +16,6 @@
package com.android.systemui.keyguard.ui.viewmodel
-import android.util.MathUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromDozingTransitionInteractor.Companion.TO_GONE_DURATION
import com.android.systemui.keyguard.shared.model.Edge
@@ -50,12 +49,10 @@
)
fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> {
- var startAlpha = 1f
return transitionAnimation.sharedFlow(
duration = 200.milliseconds,
- onStart = { startAlpha = viewState.alpha() },
- onStep = { MathUtils.lerp(startAlpha, 0f, it) },
- onFinish = { 0f },
+ onStart = { 0f },
+ onStep = { 0f },
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModel.kt
index 70c0032..c62e4f4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModel.kt
@@ -60,6 +60,14 @@
onFinish = { 1f },
)
+ val notificationAlpha: Flow<Float> =
+ transitionAnimation.sharedFlow(
+ duration = 500.milliseconds,
+ onStep = { 1f - it },
+ // Needs to be 1f in order for HUNs to appear on AOD
+ onFinish = { 1f },
+ )
+
val deviceEntryBackgroundViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(0f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt
index 7ac03bf..c6efcfa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt
@@ -130,6 +130,6 @@
companion object {
private const val TAG = "KeyguardBlueprintViewModel"
- private const val DEBUG = true
+ private const val DEBUG = false
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
index 940f423..f5c521a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
@@ -32,6 +32,7 @@
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
import com.android.systemui.statusbar.ui.SystemBarUtilsProxy
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -48,6 +49,7 @@
constructor(
keyguardClockInteractor: KeyguardClockInteractor,
@Application private val applicationScope: CoroutineScope,
+ aodNotificationIconViewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
notifsKeyguardInteractor: NotificationsKeyguardInteractor,
@get:VisibleForTesting val shadeInteractor: ShadeInteractor,
private val systemBarUtils: SystemBarUtilsProxy,
@@ -105,8 +107,13 @@
initialValue = false
)
- val isAodIconsVisible: StateFlow<Boolean> =
- notifsKeyguardInteractor.areNotificationsFullyHidden.stateIn(
+ // To translate elements below smartspace in weather clock to avoid overlapping between date
+ // element in weather clock and aod icons
+ val isAodIconsVisible: StateFlow<Boolean> = combine(aodNotificationIconViewModel.icons.map {
+ it.visibleIcons.isNotEmpty()
+ }, notifsKeyguardInteractor.areNotificationsFullyHidden) { hasIcons, visible ->
+ hasIcons && visible
+ }.stateIn(
scope = applicationScope,
started = SharingStarted.WhileSubscribed(),
initialValue = false
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index ee52ad0..aefff7d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -66,7 +66,6 @@
import kotlinx.coroutines.flow.combineTransform
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onStart
@@ -203,7 +202,7 @@
combine(
communalInteractor.isIdleOnCommunal,
keyguardTransitionInteractor
- .transitionValue(GONE)
+ .transitionValue(scene = Scenes.Gone, stateWithoutSceneContainer = GONE)
.map { it == 1f }
.onStart { emit(false) },
keyguardTransitionInteractor
@@ -236,7 +235,7 @@
// value emitted by any of them. Do not add flows that cannot make this guarantee.
merge(
alphaOnShadeExpansion,
- keyguardInteractor.dismissAlpha.filterNotNull(),
+ keyguardInteractor.dismissAlpha,
alternateBouncerToGoneTransitionViewModel.lockscreenAlpha(viewState),
aodToGoneTransitionViewModel.lockscreenAlpha(viewState),
aodToLockscreenTransitionViewModel.lockscreenAlpha(viewState),
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
index 02e48fc..10cfd6b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
@@ -27,11 +27,13 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
+import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
+import com.android.systemui.util.kotlin.filterValuesNotNull
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -89,37 +91,36 @@
isCommunalAvailable: Boolean,
shadeMode: ShadeMode,
): Map<UserAction, UserActionResult> {
- val shadeSceneKey =
+ val notifShadeSceneKey =
UserActionResult(
- toScene =
- if (shadeMode is ShadeMode.Dual) Scenes.NotificationsShade else Scenes.Shade,
+ toScene = SceneFamilies.NotifShade,
transitionKey = ToSplitShade.takeIf { shadeMode is ShadeMode.Split },
)
- val quickSettingsIfSingleShade =
- if (shadeMode is ShadeMode.Single) UserActionResult(Scenes.QuickSettings)
- else shadeSceneKey
-
return mapOf(
Swipe.Left to UserActionResult(Scenes.Communal).takeIf { isCommunalAvailable },
Swipe.Up to if (isDeviceUnlocked) Scenes.Gone else Scenes.Bouncer,
// Swiping down from the top edge goes to QS (or shade if in split shade mode).
- swipeDownFromTop(pointerCount = 1) to quickSettingsIfSingleShade,
- swipeDownFromTop(pointerCount = 2) to
- // TODO(b/338577208): Remove 'Dual' once we add Dual Shade invocation zones.
- if (shadeMode is ShadeMode.Dual) {
- UserActionResult(Scenes.QuickSettingsShade)
+ swipeDownFromTop(pointerCount = 1) to
+ if (shadeMode is ShadeMode.Single) {
+ UserActionResult(Scenes.QuickSettings)
} else {
- quickSettingsIfSingleShade
+ notifShadeSceneKey
},
- // Swiping down, not from the edge, always navigates to the shade scene.
- swipeDown(pointerCount = 1) to shadeSceneKey,
- swipeDown(pointerCount = 2) to shadeSceneKey,
+ // TODO(b/338577208): Remove once we add Dual Shade invocation zones.
+ swipeDownFromTop(pointerCount = 2) to
+ UserActionResult(
+ toScene = SceneFamilies.QuickSettings,
+ transitionKey = ToSplitShade.takeIf { shadeMode is ShadeMode.Split }
+ ),
+
+ // Swiping down, not from the edge, always navigates to the notif shade scene.
+ swipeDown(pointerCount = 1) to notifShadeSceneKey,
+ swipeDown(pointerCount = 2) to notifShadeSceneKey,
)
- .filterValues { it != null }
- .mapValues { checkNotNull(it.value) }
+ .filterValuesNotNull()
}
private fun swipeDownFromTop(pointerCount: Int): Swipe {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt b/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt
index 0c70f10..6a91d1b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt
@@ -218,9 +218,9 @@
mediaFromRecPackageName = null
_currentMedia.value = sortedMap.values.toList()
}
- } else if (sortedMap.size > sortedMedia.size) {
+ } else if (sortedMap.size > _currentMedia.value.size && it.active) {
_currentMedia.value = sortedMap.values.toList()
- } else if (sortedMap.size == sortedMedia.size) {
+ } else {
// When loading an update for an existing media control.
val currentList =
mutableListOf<MediaCommonModel>().apply { addAll(_currentMedia.value) }
@@ -296,6 +296,18 @@
mediaFromRecPackageName = packageName
}
+ fun hasActiveMedia(): Boolean {
+ return _selectedUserEntries.value.any { it.value.active }
+ }
+
+ fun hasAnyMedia(): Boolean {
+ return _selectedUserEntries.value.entries.isNotEmpty()
+ }
+
+ fun isRecommendationActive(): Boolean {
+ return _smartspaceMediaData.value.isActive
+ }
+
private fun canBeRemoved(data: MediaData): Boolean {
return data.isPlaying?.let { !it } ?: data.isClearable && !data.active
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
index aa93df7..8b2f619 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
@@ -358,7 +358,12 @@
// LocalMediaManager. Override with routing session name if available to
// show dynamic group name.
connectedDevice?.copy(name = it.name ?: connectedDevice.name)
- }
+ } ?: MediaDeviceData(
+ enabled = false,
+ icon = context.getDrawable(R.drawable.ic_media_home_devices),
+ name = context.getString(R.string.media_seamless_other_device),
+ showBroadcastButton = false
+ )
} else {
// Prefer SASS if available when playback is local.
activeDevice = getSassDevice() ?: connectedDevice
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
index b4bd4fd..0630cbd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
@@ -25,7 +25,6 @@
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.media.controls.data.repository.MediaDataRepository
import com.android.systemui.media.controls.data.repository.MediaFilterRepository
import com.android.systemui.media.controls.domain.pipeline.MediaDataCombineLatest
import com.android.systemui.media.controls.domain.pipeline.MediaDataFilterImpl
@@ -45,7 +44,6 @@
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.stateIn
/** Encapsulates business logic for media pipeline. */
@@ -55,7 +53,6 @@
@Inject
constructor(
@Application applicationScope: CoroutineScope,
- private val mediaDataRepository: MediaDataRepository,
private val mediaDataProcessor: MediaDataProcessor,
private val mediaTimeoutListener: MediaTimeoutListener,
private val mediaResumeListener: MediaResumeListener,
@@ -103,26 +100,6 @@
initialValue = false,
)
- /** Are there any media notifications active, excluding the recommendations? */
- val hasActiveMedia: StateFlow<Boolean> =
- mediaFilterRepository.selectedUserEntries
- .mapLatest { entries -> entries.any { it.value.active } }
- .stateIn(
- scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = false,
- )
-
- /** Are there any media notifications, excluding the recommendations? */
- val hasAnyMedia: StateFlow<Boolean> =
- mediaFilterRepository.selectedUserEntries
- .mapLatest { entries -> entries.isNotEmpty() }
- .stateIn(
- scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = false,
- )
-
/** The current list for user media instances */
val currentMedia: StateFlow<List<MediaCommonModel>> = mediaFilterRepository.currentMedia
@@ -235,11 +212,11 @@
override fun hasAnyMediaOrRecommendation() = hasAnyMediaOrRecommendation.value
- override fun hasActiveMedia() = hasActiveMedia.value
+ override fun hasActiveMedia() = mediaFilterRepository.hasActiveMedia()
- override fun hasAnyMedia() = hasAnyMedia.value
+ override fun hasAnyMedia() = mediaFilterRepository.hasAnyMedia()
- override fun isRecommendationActive() = mediaDataRepository.smartspaceMediaData.value.isActive
+ override fun isRecommendationActive() = mediaFilterRepository.isRecommendationActive()
fun reorderMedia() {
mediaFilterRepository.setOrderedMedia()
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
index 8f15561..53794d2 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
@@ -106,7 +106,6 @@
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.launch
@@ -216,7 +215,7 @@
private var carouselLocale: Locale? = null
private val animationScaleObserver: ContentObserver =
- object : ContentObserver(null) {
+ object : ContentObserver(executor, 0) {
override fun onChange(selfChange: Boolean) {
if (!mediaFlags.isSceneContainerEnabled()) {
MediaPlayerData.players().forEach { it.updateAnimatorDurationScale() }
@@ -396,10 +395,12 @@
}
// Notifies all active players about animation scale changes.
- globalSettings.registerContentObserverSync(
- Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE),
- animationScaleObserver
- )
+ bgExecutor.execute {
+ globalSettings.registerContentObserverSync(
+ Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE),
+ animationScaleObserver
+ )
+ }
}
private fun setUpListeners() {
@@ -695,7 +696,6 @@
.onStart { emit(Unit) }
.map { getMediaLockScreenSetting() }
.distinctUntilChanged()
- .flowOn(backgroundDispatcher)
.collectLatest {
allowMediaPlayerOnLockScreen = it
updateHostVisibility()
@@ -884,7 +884,8 @@
val previousVisibleIndex =
MediaPlayerData.playerKeys().indexOfFirst { key -> it == key }
mediaCarouselScrollHandler.scrollToPlayer(previousVisibleIndex, mediaIndex)
- } ?: mediaCarouselScrollHandler.scrollToPlayer(destIndex = mediaIndex)
+ }
+ ?: mediaCarouselScrollHandler.scrollToPlayer(destIndex = mediaIndex)
}
} else if (isRtl && mediaContent.childCount > 0) {
// In RTL, Scroll to the first player as it is the rightmost player in media carousel.
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
index 601d563..88a28bf 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
@@ -36,6 +36,7 @@
import com.android.app.animation.Interpolators
import com.android.app.tracing.traceSection
import com.android.keyguard.KeyguardViewController
+import com.android.systemui.Flags.mediaControlsLockscreenShadeBugFix
import com.android.systemui.communal.ui.viewmodel.CommunalTransitionViewModel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -483,8 +484,7 @@
object : StatusBarStateController.StateListener {
override fun onStatePreChange(oldState: Int, newState: Int) {
// We're updating the location before the state change happens, since we want
- // the
- // location of the previous state to still be up to date when the animation
+ // the location of the previous state to still be up to date when the animation
// starts
if (
newState == StatusBarState.SHADE_LOCKED &&
@@ -588,6 +588,17 @@
}
}
+ if (mediaControlsLockscreenShadeBugFix()) {
+ coroutineScope.launch {
+ shadeInteractor.shadeExpansion.collect { expansion ->
+ if (expansion >= 1f || expansion <= 0f) {
+ // Shade has fully expanded or collapsed: force transition amount update
+ setTransitionToFullShadeAmount(expansion)
+ }
+ }
+ }
+ }
+
val settingsObserver: ContentObserver =
object : ContentObserver(handler) {
override fun onChange(selfChange: Boolean, uri: Uri?) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
index 4e90936..315a9fb 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
@@ -201,6 +201,7 @@
) {
if (immediatelyRemove || isReorderingAllowed()) {
interactor.dismissSmartspaceRecommendation(commonModel.recsLoadingModel.key, 0L)
+ mediaRecs = null
if (!immediatelyRemove) {
// Although it wasn't requested, we were able to process the removal
// immediately since reordering is allowed. So, notify hosts to update
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaItem.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaItem.java
index fbb84de..4496b25 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaItem.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaItem.java
@@ -16,7 +16,9 @@
package com.android.systemui.media.dialog;
-import androidx.annotation.IntDef;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import com.android.settingslib.media.MediaDevice;
import com.android.systemui.res.R;
@@ -46,40 +48,50 @@
int TYPE_PAIR_NEW_DEVICE = 2;
}
- public MediaItem() {
- this.mMediaDeviceOptional = Optional.empty();
- this.mTitle = null;
- this.mMediaItemType = MediaItemType.TYPE_PAIR_NEW_DEVICE;
+ /**
+ * Returns a new {@link MediaItemType#TYPE_DEVICE} {@link MediaItem} with its {@link
+ * #getMediaDevice() media device} set to {@code device} and its title set to {@code device}'s
+ * name.
+ */
+ public static MediaItem createDeviceMediaItem(@NonNull MediaDevice device) {
+ return new MediaItem(device, device.getName(), MediaItemType.TYPE_DEVICE);
}
- public MediaItem(String title, int mediaItemType) {
- this.mMediaDeviceOptional = Optional.empty();
+ /**
+ * Returns a new {@link MediaItemType#TYPE_PAIR_NEW_DEVICE} {@link MediaItem} with both {@link
+ * #getMediaDevice() media device} and title set to {@code null}.
+ */
+ public static MediaItem createPairNewDeviceMediaItem() {
+ return new MediaItem(
+ /* device */ null, /* title */ null, MediaItemType.TYPE_PAIR_NEW_DEVICE);
+ }
+
+ /**
+ * Returns a new {@link MediaItemType#TYPE_GROUP_DIVIDER} {@link MediaItem} with the specified
+ * title and a {@code null} {@link #getMediaDevice() media device}.
+ */
+ public static MediaItem createGroupDividerMediaItem(@Nullable String title) {
+ return new MediaItem(/* device */ null, title, MediaItemType.TYPE_GROUP_DIVIDER);
+ }
+
+ private MediaItem(
+ @Nullable MediaDevice device, @Nullable String title, @MediaItemType int type) {
+ this.mMediaDeviceOptional = Optional.ofNullable(device);
this.mTitle = title;
- this.mMediaItemType = mediaItemType;
- }
-
- public MediaItem(MediaDevice mediaDevice) {
- this.mMediaDeviceOptional = Optional.of(mediaDevice);
- this.mTitle = mediaDevice.getName();
- this.mMediaItemType = MediaItemType.TYPE_DEVICE;
+ this.mMediaItemType = type;
}
public Optional<MediaDevice> getMediaDevice() {
return mMediaDeviceOptional;
}
- /**
- * Get layout id based on media item Type.
- */
- public static int getMediaLayoutId(int mediaItemType) {
- switch (mediaItemType) {
- case MediaItemType.TYPE_DEVICE:
- case MediaItemType.TYPE_PAIR_NEW_DEVICE:
- return R.layout.media_output_list_item_advanced;
- case MediaItemType.TYPE_GROUP_DIVIDER:
- default:
- return R.layout.media_output_list_group_divider;
- }
+ /** Get layout id based on media item Type. */
+ public static int getMediaLayoutId(@MediaItemType int mediaItemType) {
+ return switch (mediaItemType) {
+ case MediaItemType.TYPE_DEVICE, MediaItemType.TYPE_PAIR_NEW_DEVICE ->
+ R.layout.media_output_list_item_advanced;
+ default -> R.layout.media_output_list_group_divider;
+ };
}
public String getTitle() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index c2cfdbe..1e86563 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -68,6 +68,7 @@
import androidx.annotation.VisibleForTesting;
import androidx.core.graphics.drawable.IconCompat;
+import com.android.internal.annotations.GuardedBy;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.Utils;
import com.android.settingslib.bluetooth.BluetoothUtils;
@@ -656,10 +657,16 @@
if (DEBUG) {
Log.d(TAG, "No connected media device or muting expected device exist.");
}
- return categorizeMediaItems(null, devices, needToHandleMutingExpectedDevice);
+ return categorizeMediaItemsLocked(
+ /* connectedMediaDevice */ null,
+ devices,
+ needToHandleMutingExpectedDevice);
}
// selected device exist
- return categorizeMediaItems(connectedMediaDevice, devices, false);
+ return categorizeMediaItemsLocked(
+ connectedMediaDevice,
+ devices,
+ /* needToHandleMutingExpectedDevice */ false);
}
// To keep the same list order
final List<MediaDevice> targetMediaDevices = new ArrayList<>();
@@ -682,8 +689,9 @@
devices.removeAll(targetMediaDevices);
targetMediaDevices.addAll(devices);
}
- List<MediaItem> finalMediaItems = targetMediaDevices.stream().map(
- MediaItem::new).collect(Collectors.toList());
+ List<MediaItem> finalMediaItems = targetMediaDevices.stream()
+ .map(MediaItem::createDeviceMediaItem)
+ .collect(Collectors.toList());
dividerItems.forEach(finalMediaItems::add);
attachConnectNewDeviceItemIfNeeded(finalMediaItems);
return finalMediaItems;
@@ -694,51 +702,50 @@
* Initial categorization of current devices, will not be called for updates to the devices
* list.
*/
- private List<MediaItem> categorizeMediaItems(MediaDevice connectedMediaDevice,
+ @GuardedBy("mMediaDevicesLock")
+ private List<MediaItem> categorizeMediaItemsLocked(MediaDevice connectedMediaDevice,
List<MediaDevice> devices,
boolean needToHandleMutingExpectedDevice) {
- synchronized (mMediaDevicesLock) {
- List<MediaItem> finalMediaItems = new ArrayList<>();
- Set<String> selectedDevicesIds = getSelectedMediaDevice().stream().map(
- MediaDevice::getId).collect(Collectors.toSet());
- if (connectedMediaDevice != null) {
- selectedDevicesIds.add(connectedMediaDevice.getId());
- }
- boolean suggestedDeviceAdded = false;
- boolean displayGroupAdded = false;
- for (MediaDevice device : devices) {
- if (needToHandleMutingExpectedDevice && device.isMutingExpectedDevice()) {
- finalMediaItems.add(0, new MediaItem(device));
- } else if (!needToHandleMutingExpectedDevice && selectedDevicesIds.contains(
- device.getId())) {
- finalMediaItems.add(0, new MediaItem(device));
- } else {
- if (device.isSuggestedDevice() && !suggestedDeviceAdded) {
- attachGroupDivider(finalMediaItems, mContext.getString(
- R.string.media_output_group_title_suggested_device));
- suggestedDeviceAdded = true;
- } else if (!device.isSuggestedDevice() && !displayGroupAdded) {
- attachGroupDivider(finalMediaItems, mContext.getString(
- R.string.media_output_group_title_speakers_and_displays));
- displayGroupAdded = true;
- }
- finalMediaItems.add(new MediaItem(device));
- }
- }
- attachConnectNewDeviceItemIfNeeded(finalMediaItems);
- return finalMediaItems;
+ List<MediaItem> finalMediaItems = new ArrayList<>();
+ Set<String> selectedDevicesIds = getSelectedMediaDevice().stream()
+ .map(MediaDevice::getId)
+ .collect(Collectors.toSet());
+ if (connectedMediaDevice != null) {
+ selectedDevicesIds.add(connectedMediaDevice.getId());
}
+ boolean suggestedDeviceAdded = false;
+ boolean displayGroupAdded = false;
+ for (MediaDevice device : devices) {
+ if (needToHandleMutingExpectedDevice && device.isMutingExpectedDevice()) {
+ finalMediaItems.add(0, MediaItem.createDeviceMediaItem(device));
+ } else if (!needToHandleMutingExpectedDevice && selectedDevicesIds.contains(
+ device.getId())) {
+ finalMediaItems.add(0, MediaItem.createDeviceMediaItem(device));
+ } else {
+ if (device.isSuggestedDevice() && !suggestedDeviceAdded) {
+ attachGroupDivider(finalMediaItems, mContext.getString(
+ R.string.media_output_group_title_suggested_device));
+ suggestedDeviceAdded = true;
+ } else if (!device.isSuggestedDevice() && !displayGroupAdded) {
+ attachGroupDivider(finalMediaItems, mContext.getString(
+ R.string.media_output_group_title_speakers_and_displays));
+ displayGroupAdded = true;
+ }
+ finalMediaItems.add(MediaItem.createDeviceMediaItem(device));
+ }
+ }
+ attachConnectNewDeviceItemIfNeeded(finalMediaItems);
+ return finalMediaItems;
}
private void attachGroupDivider(List<MediaItem> mediaItems, String title) {
- mediaItems.add(
- new MediaItem(title, MediaItem.MediaItemType.TYPE_GROUP_DIVIDER));
+ mediaItems.add(MediaItem.createGroupDividerMediaItem(title));
}
private void attachConnectNewDeviceItemIfNeeded(List<MediaItem> mediaItems) {
// Attach "Connect a device" item only when current output is not remote and not a group
if (!isCurrentConnectedDeviceRemote() && getSelectedMediaDevice().size() == 1) {
- mediaItems.add(new MediaItem());
+ mediaItems.add(MediaItem.createPairNewDeviceMediaItem());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManager.kt b/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManager.kt
index f47954a..3a292e7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManager.kt
@@ -94,7 +94,7 @@
}
private fun AudioDeviceAttributes.getIcon(): Drawable {
- return deviceIconUtil.getIconFromAudioDeviceType(this.type, context)
+ return deviceIconUtil.getIconFromAudioDeviceType(this.type)
}
private fun IntArray.hasMedia() = USAGE_MEDIA in this
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
index 03adf1b..01b1be9 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
@@ -58,7 +58,7 @@
// opening the app selector in split screen mode, the foreground task will be the second
// task in index 0.
val foregroundGroup =
- if (groupedTasks.first().splitBounds != null) groupedTasks.first()
+ if (groupedTasks.firstOrNull()?.splitBounds != null) groupedTasks.first()
else groupedTasks.elementAtOrNull(1)
val foregroundTaskId1 = foregroundGroup?.taskInfo1?.taskId
val foregroundTaskId2 = foregroundGroup?.taskInfo2?.taskId
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/model/MediaProjectionState.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/model/MediaProjectionState.kt
index 1d5f6f5..de300b2 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/model/MediaProjectionState.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/model/MediaProjectionState.kt
@@ -20,7 +20,21 @@
/** Represents the state of media projection. */
sealed interface MediaProjectionState {
- object NotProjecting : MediaProjectionState
- object EntireScreen : MediaProjectionState
- data class SingleTask(val task: RunningTaskInfo) : MediaProjectionState
+ /** There is no media being projected. */
+ data object NotProjecting : MediaProjectionState
+
+ /**
+ * Media is currently being projected.
+ *
+ * @property hostPackage the package name of the app that is receiving the content of the media
+ * projection (aka which app the phone screen contents are being sent to).
+ */
+ sealed class Projecting(open val hostPackage: String) : MediaProjectionState {
+ /** The entire screen is being projected. */
+ data class EntireScreen(override val hostPackage: String) : Projecting(hostPackage)
+
+ /** Only a single task is being projected. */
+ data class SingleTask(override val hostPackage: String, val task: RunningTaskInfo) :
+ Projecting(hostPackage)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt
index 3ce0a1e0..8a9adc7 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt
@@ -64,6 +64,10 @@
}
}
+ override suspend fun stopProjecting() {
+ withContext(backgroundDispatcher) { mediaProjectionManager.stopActiveProjection() }
+ }
+
override val mediaProjectionState: Flow<MediaProjectionState> =
conflatedCallbackFlow {
val callback =
@@ -83,7 +87,9 @@
session: ContentRecordingSession?
) {
Log.d(TAG, "MediaProjectionManager.Callback#onSessionStarted: $session")
- launch { trySendWithFailureLogging(stateForSession(session), TAG) }
+ launch {
+ trySendWithFailureLogging(stateForSession(info, session), TAG)
+ }
}
}
mediaProjectionManager.addCallback(callback, handler)
@@ -95,19 +101,23 @@
initialValue = MediaProjectionState.NotProjecting,
)
- private suspend fun stateForSession(session: ContentRecordingSession?): MediaProjectionState {
+ private suspend fun stateForSession(
+ info: MediaProjectionInfo,
+ session: ContentRecordingSession?
+ ): MediaProjectionState {
if (session == null) {
return MediaProjectionState.NotProjecting
}
+
+ val hostPackage = info.packageName
if (session.contentToRecord == RECORD_CONTENT_DISPLAY || session.tokenToRecord == null) {
- return MediaProjectionState.EntireScreen
+ return MediaProjectionState.Projecting.EntireScreen(hostPackage)
}
val matchingTask =
tasksRepository.findRunningTaskFromWindowContainerToken(
checkNotNull(session.tokenToRecord)
- )
- ?: return MediaProjectionState.EntireScreen
- return MediaProjectionState.SingleTask(matchingTask)
+ ) ?: return MediaProjectionState.Projecting.EntireScreen(hostPackage)
+ return MediaProjectionState.Projecting.SingleTask(hostPackage, matchingTask)
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt
index 21300db..50182d7 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt
@@ -26,6 +26,9 @@
/** Switches the task that should be projected. */
suspend fun switchProjectedTask(task: RunningTaskInfo)
+ /** Stops the currently active projection. */
+ suspend fun stopProjecting()
+
/** Represents the current [MediaProjectionState]. */
val mediaProjectionState: Flow<MediaProjectionState>
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index e861ddf..f004c3a 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -23,6 +23,7 @@
import static android.media.projection.MediaProjectionManager.OVERRIDE_DISABLE_MEDIA_PROJECTION_SINGLE_APP_OPTION;
import static android.media.projection.ReviewGrantedConsentResult.RECORD_CANCEL;
import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_DISPLAY;
+import static android.os.UserHandle.USER_SYSTEM;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static com.android.systemui.mediaprojection.permission.ScreenShareOptionKt.ENTIRE_SCREEN;
@@ -31,7 +32,6 @@
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.app.Activity;
-import android.app.ActivityManager;
import android.app.ActivityOptions.LaunchCookie;
import android.app.AlertDialog;
import android.app.StatusBarManager;
@@ -366,11 +366,11 @@
intent.putExtra(EXTRA_USER_REVIEW_GRANTED_CONSENT, mReviewGrantedConsentRequired);
intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
- // Start activity from the current foreground user to avoid creating a separate
- // SystemUI process without access to recent tasks because it won't have
- // WM Shell running inside.
+ // Start activity as system user and manually show app selector to all users to
+ // avoid creating a separate SystemUI process without access to recent tasks
+ // because it won't have WM Shell running inside.
mUserSelectingTask = true;
- startActivityAsUser(intent, UserHandle.of(ActivityManager.getCurrentUser()));
+ startActivityAsUser(intent, UserHandle.of(USER_SYSTEM));
// close shade if it's open
mStatusBarManager.collapsePanels();
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt
index c232d4d..118639c 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt
@@ -57,7 +57,7 @@
mediaProjectionRepository.mediaProjectionState.flatMapLatest { projectionState ->
Log.d(TAG, "MediaProjectionState -> $projectionState")
when (projectionState) {
- is MediaProjectionState.SingleTask -> {
+ is MediaProjectionState.Projecting.SingleTask -> {
val projectedTask = projectionState.task
tasksRepository.foregroundTask.map { foregroundTask ->
if (hasForegroundTaskSwitched(projectedTask, foregroundTask)) {
@@ -67,7 +67,7 @@
}
}
}
- is MediaProjectionState.EntireScreen,
+ is MediaProjectionState.Projecting.EntireScreen,
is MediaProjectionState.NotProjecting -> {
flowOf(TaskSwitchState.NotProjectingTask)
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 0e819c2..07289cb 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -2028,12 +2028,15 @@
getBarTransitions().setBackgroundOverrideAlpha(1f);
}
}
- updateScreenPinningGestures();
+
+ // Update the window layout params when the nav mode changes as that will affect the
+ // system gesture insets
+ setNavBarMode(mode);
+ repositionNavigationBar(mCurrentRotation);
if (!canShowSecondaryHandle()) {
resetSecondaryHandle();
}
- setNavBarMode(mode);
mView.setShouldShowSwipeUpUi(mOverviewProxyService.shouldShowSwipeUpUI());
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 41cd2c4..99c95b5 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -24,6 +24,7 @@
import static com.android.systemui.classifier.Classifier.BACK_GESTURE;
import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadScroll;
import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadThreeFingerSwipe;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED;
import static java.util.stream.Collectors.joining;
@@ -1021,8 +1022,10 @@
if (mIsTrackpadThreeFingerSwipe) {
// Trackpad back gestures don't have zones, so we don't need to check if the down
// event is within insets.
- mAllowGesture = isBackAllowedCommon && isValidTrackpadBackGesture(
- true /* isTrackpadEvent */);
+ boolean trackpadGesturesEnabled =
+ (mSysUiFlags & SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED) == 0;
+ mAllowGesture = isBackAllowedCommon && trackpadGesturesEnabled
+ && isValidTrackpadBackGesture(true /* isTrackpadEvent */);
} else {
mAllowGesture = isBackAllowedCommon && !mUsingThreeButtonNav && isWithinInsets
&& isWithinTouchRegion((int) ev.getX(), (int) ev.getY())
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index f3cc35ba..abc2b7f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -217,10 +217,13 @@
private void setBrightnessViewMargin() {
if (mBrightnessView != null) {
MarginLayoutParams lp = (MarginLayoutParams) mBrightnessView.getLayoutParams();
+ // For Brightness Slider to extend its boundary to draw focus background
+ int offset = getResources()
+ .getDimensionPixelSize(R.dimen.rounded_slider_boundary_offset);
lp.topMargin = mContext.getResources()
- .getDimensionPixelSize(R.dimen.qs_brightness_margin_top);
+ .getDimensionPixelSize(R.dimen.qs_brightness_margin_top) - offset;
lp.bottomMargin = mContext.getResources()
- .getDimensionPixelSize(R.dimen.qs_brightness_margin_bottom);
+ .getDimensionPixelSize(R.dimen.qs_brightness_margin_bottom) - offset;
mBrightnessView.setLayoutParams(lp);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
index ea89be6..b705a03 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -21,7 +21,6 @@
import android.content.Context;
import android.os.Handler;
-import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogModule;
import com.android.systemui.dagger.NightDisplayListenerModule;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
@@ -61,7 +60,6 @@
*/
@Module(subcomponents = {QSFragmentComponent.class, QSSceneComponent.class},
includes = {
- BluetoothTileDialogModule.class,
MediaModule.class,
PanelsModule.class,
QSExternalModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt
index e1b21ef..9233e76 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt
@@ -16,7 +16,6 @@
package com.android.systemui.qs.panels.ui.compose
-import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -40,6 +39,13 @@
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawWithContent
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Path
+import androidx.compose.ui.graphics.PathEffect
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.addOutline
+import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Dp
@@ -268,10 +274,9 @@
private fun CurrentTilesContainer(content: @Composable () -> Unit) {
Box(
Modifier.fillMaxWidth()
- .border(
- width = 1.dp,
- color = MaterialTheme.colorScheme.onBackground,
- shape = RoundedCornerShape(dimensionResource(R.dimen.qs_corner_radius))
+ .dashedBorder(
+ color = MaterialTheme.colorScheme.onBackground.copy(alpha = .5f),
+ shape = Dimensions.ContainerShape,
)
.padding(dimensionResource(R.dimen.qs_tile_margin_vertical))
) {
@@ -286,7 +291,7 @@
.background(
color = MaterialTheme.colorScheme.background,
alpha = { 1f },
- shape = RoundedCornerShape(dimensionResource(R.dimen.qs_corner_radius))
+ shape = Dimensions.ContainerShape,
)
.padding(dimensionResource(R.dimen.qs_tile_margin_vertical))
) {
@@ -305,4 +310,27 @@
item(span = { GridItemSpan(maxCurrentLineSpan) }) { Spacer(Modifier) }
}
}
+
+ private fun Modifier.dashedBorder(
+ color: Color,
+ shape: Shape,
+ ): Modifier {
+ return this.drawWithContent {
+ val outline = shape.createOutline(size, layoutDirection, this)
+ val path = Path()
+ path.addOutline(outline)
+ val stroke =
+ Stroke(
+ width = 1.dp.toPx(),
+ pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f))
+ )
+ this.drawContent()
+ drawPath(path = path, style = stroke, color = color)
+ }
+ }
+
+ private object Dimensions {
+ // Corner radius is half the height of a tile + padding
+ val ContainerShape = RoundedCornerShape(48.dp)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
index e4fbb4b..bbb98d3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalFoundationApi::class)
+
package com.android.systemui.qs.panels.ui.compose
import android.graphics.drawable.Animatable
@@ -27,17 +29,17 @@
import androidx.compose.animation.graphics.vector.AnimatedImageVector
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
-import androidx.compose.foundation.background
import androidx.compose.foundation.basicMarquee
-import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Arrangement.spacedBy
import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxHeight
-import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
@@ -46,11 +48,6 @@
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.shape.CircleShape
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Add
-import androidx.compose.material.icons.filled.Remove
-import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -68,7 +65,6 @@
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.onClick
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.stateDescription
@@ -78,9 +74,11 @@
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.Expandable
+import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.common.ui.compose.load
+import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.panels.ui.viewmodel.AvailableEditActions
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
import com.android.systemui.qs.panels.ui.viewmodel.TileUiState
@@ -90,13 +88,14 @@
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.res.R
+import java.util.function.Supplier
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.mapLatest
object TileType
-@OptIn(ExperimentalCoroutinesApi::class, ExperimentalFoundationApi::class)
+@OptIn(ExperimentalCoroutinesApi::class)
@Composable
fun Tile(
tile: TileViewModel,
@@ -110,46 +109,143 @@
.collectAsStateWithLifecycle(tile.currentState.toUiState())
val colors = TileDefaults.getColorForState(state.state)
- val context = LocalContext.current
-
- Expandable(
- color = colors.background,
- shape = RoundedCornerShape(dimensionResource(R.dimen.qs_corner_radius)),
+ TileContainer(
+ colors = colors,
+ showLabels = showLabels,
+ label = state.label.toString(),
+ iconOnly = iconOnly,
+ onClick = tile::onClick,
+ onLongClick = tile::onLongClick,
+ modifier = modifier,
) {
- Row(
- modifier =
- modifier
- .combinedClickable(
- onClick = { tile.onClick(it) },
- onLongClick = { tile.onLongClick(it) }
- )
- .tileModifier(colors),
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = tileHorizontalArrangement(iconOnly),
- ) {
- val icon =
- remember(state.icon) {
- state.icon.get().let {
- if (it is QSTileImpl.ResourceIcon) {
- Icon.Resource(it.resId, null)
- } else {
- Icon.Loaded(it.getDrawable(context), null)
- }
- }
- }
- TileContent(
+ val icon = getTileIcon(icon = state.icon)
+ if (iconOnly) {
+ TileIcon(icon = icon, color = colors.icon, modifier = Modifier.align(Alignment.Center))
+ } else {
+ LargeTileContent(
label = state.label.toString(),
secondaryLabel = state.secondaryLabel.toString(),
icon = icon,
colors = colors,
- iconOnly = iconOnly,
- showLabels = showLabels,
+ onClick = tile::onSecondaryClick,
+ onLongClick = tile::onLongClick,
)
}
}
}
@Composable
+private fun TileContainer(
+ colors: TileColors,
+ showLabels: Boolean,
+ label: String,
+ iconOnly: Boolean,
+ clickEnabled: Boolean = true,
+ onClick: (Expandable) -> Unit = {},
+ onLongClick: (Expandable) -> Unit = {},
+ modifier: Modifier = Modifier,
+ content: @Composable BoxScope.() -> Unit,
+) {
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement =
+ spacedBy(dimensionResource(id = R.dimen.qs_label_container_margin), Alignment.Top),
+ modifier = modifier,
+ ) {
+ val backgroundColor =
+ if (iconOnly) {
+ colors.iconBackground
+ } else {
+ colors.background
+ }
+ Expandable(
+ color = backgroundColor,
+ shape = TileDefaults.TileShape,
+ modifier =
+ Modifier.height(dimensionResource(id = R.dimen.qs_tile_height))
+ .clip(TileDefaults.TileShape)
+ ) {
+ Box(
+ modifier =
+ Modifier.fillMaxSize()
+ .combinedClickable(
+ enabled = clickEnabled,
+ onClick = { onClick(it) },
+ onLongClick = { onLongClick(it) }
+ )
+ .tilePadding(),
+ ) {
+ content()
+ }
+ }
+
+ if (showLabels && iconOnly) {
+ Text(
+ label,
+ maxLines = 2,
+ color = colors.label,
+ overflow = TextOverflow.Ellipsis,
+ textAlign = TextAlign.Center,
+ )
+ }
+ }
+}
+
+@Composable
+private fun LargeTileContent(
+ label: String,
+ secondaryLabel: String?,
+ icon: Icon,
+ colors: TileColors,
+ clickEnabled: Boolean = true,
+ onClick: (Expandable) -> Unit = {},
+ onLongClick: (Expandable) -> Unit = {},
+) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = tileHorizontalArrangement()
+ ) {
+ Expandable(
+ color = colors.iconBackground,
+ shape = TileDefaults.TileShape,
+ modifier = Modifier.fillMaxHeight().aspectRatio(1f)
+ ) {
+ Box(
+ modifier =
+ Modifier.fillMaxSize()
+ .clip(TileDefaults.TileShape)
+ .combinedClickable(
+ enabled = clickEnabled,
+ onClick = { onClick(it) },
+ onLongClick = { onLongClick(it) }
+ )
+ ) {
+ TileIcon(
+ icon = icon,
+ color = colors.icon,
+ modifier = Modifier.align(Alignment.Center)
+ )
+ }
+ }
+
+ Column(verticalArrangement = Arrangement.Center, modifier = Modifier.fillMaxHeight()) {
+ Text(
+ label,
+ color = colors.label,
+ modifier = Modifier.basicMarquee(),
+ )
+ if (!TextUtils.isEmpty(secondaryLabel)) {
+ Text(
+ secondaryLabel ?: "",
+ color = colors.secondaryLabel,
+ modifier = Modifier.basicMarquee(),
+ )
+ }
+ }
+ }
+}
+
+@Composable
fun TileLazyGrid(
modifier: Modifier = Modifier,
columns: GridCells,
@@ -247,41 +343,19 @@
""
}
- Box(
+ val iconOnly = isIconOnly(viewModel.tileSpec)
+ val tileHeight = tileHeight(iconOnly && showLabels)
+ EditTile(
+ tileViewModel = viewModel,
+ iconOnly = iconOnly,
+ showLabels = showLabels,
+ clickEnabled = canClick,
+ onClick = { onClick.invoke(viewModel.tileSpec) },
modifier =
- Modifier.clickable(enabled = canClick) { onClick.invoke(viewModel.tileSpec) }
- .animateItem()
- .semantics {
- onClick(onClickActionName) { false }
- this.stateDescription = stateDescription
- }
- ) {
- val iconOnly = isIconOnly(viewModel.tileSpec)
- val tileHeight = tileHeight(iconOnly && showLabels)
- EditTile(
- tileViewModel = viewModel,
- iconOnly = iconOnly,
- showLabels = showLabels,
- modifier = Modifier.height(tileHeight)
- )
- if (canClick) {
- Badge(clickAction, Modifier.align(Alignment.TopEnd))
- }
- }
- }
-}
-
-@Composable
-fun Badge(action: ClickAction, modifier: Modifier = Modifier) {
- Box(modifier = modifier.size(16.dp).background(Color.Cyan, shape = CircleShape)) {
- Icon(
- imageVector =
- when (action) {
- ClickAction.ADD -> Icons.Filled.Add
- ClickAction.REMOVE -> Icons.Filled.Remove
- },
- "",
- tint = Color.Black,
+ Modifier.height(tileHeight).animateItem().semantics {
+ onClick(onClickActionName) { false }
+ this.stateDescription = stateDescription
+ }
)
}
}
@@ -291,25 +365,40 @@
tileViewModel: EditTileViewModel,
iconOnly: Boolean,
showLabels: Boolean,
+ clickEnabled: Boolean,
+ onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
val label = tileViewModel.label.load() ?: tileViewModel.tileSpec.spec
val colors = TileDefaults.inactiveTileColors()
- Row(
- modifier = modifier.tileModifier(colors).semantics { this.contentDescription = label },
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = tileHorizontalArrangement(iconOnly)
+ TileContainer(
+ colors = colors,
+ showLabels = showLabels,
+ label = label,
+ iconOnly = iconOnly,
+ clickEnabled = clickEnabled,
+ onClick = { onClick() },
+ onLongClick = { onClick() },
+ modifier = modifier,
) {
- TileContent(
- label = label,
- secondaryLabel = tileViewModel.appName?.load(),
- colors = colors,
- icon = tileViewModel.icon,
- iconOnly = iconOnly,
- showLabels = showLabels,
- animateIconToEnd = true,
- )
+ if (iconOnly) {
+ TileIcon(
+ icon = tileViewModel.icon,
+ color = colors.icon,
+ modifier = Modifier.align(Alignment.Center)
+ )
+ } else {
+ LargeTileContent(
+ label = label,
+ secondaryLabel = tileViewModel.appName?.load(),
+ icon = tileViewModel.icon,
+ colors = colors,
+ clickEnabled = clickEnabled,
+ onClick = { onClick() },
+ onLongClick = { onClick() },
+ )
+ }
}
}
@@ -318,14 +407,27 @@
REMOVE,
}
+@Composable
+private fun getTileIcon(icon: Supplier<QSTile.Icon>): Icon {
+ val context = LocalContext.current
+ return icon.get().let {
+ if (it is QSTileImpl.ResourceIcon) {
+ Icon.Resource(it.resId, null)
+ } else {
+ Icon.Loaded(it.getDrawable(context), null)
+ }
+ }
+}
+
@OptIn(ExperimentalAnimationGraphicsApi::class)
@Composable
private fun TileIcon(
icon: Icon,
color: Color,
animateToEnd: Boolean = false,
+ modifier: Modifier = Modifier,
) {
- val modifier = Modifier.size(dimensionResource(id = R.dimen.qs_icon_size))
+ val iconModifier = modifier.size(dimensionResource(id = R.dimen.qs_icon_size))
val context = LocalContext.current
val loadedDrawable =
remember(icon, context) {
@@ -338,7 +440,7 @@
Icon(
icon = icon,
tint = color,
- modifier = modifier,
+ modifier = iconModifier,
)
} else if (icon is Icon.Resource) {
val image = AnimatedImageVector.animatedVectorResource(id = icon.res)
@@ -357,80 +459,25 @@
painter = painter,
contentDescription = null,
colorFilter = ColorFilter.tint(color = color),
- modifier = modifier
+ modifier = iconModifier
)
}
}
@Composable
-private fun Modifier.tileModifier(colors: TileColors): Modifier {
- return fillMaxWidth()
- .clip(RoundedCornerShape(dimensionResource(R.dimen.qs_corner_radius)))
- .background(colors.background)
- .padding(horizontal = dimensionResource(id = R.dimen.qs_label_container_margin))
+private fun Modifier.tilePadding(): Modifier {
+ return padding(dimensionResource(id = R.dimen.qs_label_container_margin))
}
@Composable
-private fun tileHorizontalArrangement(iconOnly: Boolean): Arrangement.Horizontal {
- val horizontalAlignment =
- if (iconOnly) {
- Alignment.CenterHorizontally
- } else {
- Alignment.Start
- }
+private fun tileHorizontalArrangement(): Arrangement.Horizontal {
return spacedBy(
space = dimensionResource(id = R.dimen.qs_label_container_margin),
- alignment = horizontalAlignment
+ alignment = Alignment.Start
)
}
@Composable
-private fun TileContent(
- label: String,
- secondaryLabel: String?,
- icon: Icon,
- colors: TileColors,
- iconOnly: Boolean,
- showLabels: Boolean = false,
- animateIconToEnd: Boolean = false,
-) {
- Column(
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.Center,
- modifier = Modifier.fillMaxHeight()
- ) {
- TileIcon(icon, colors.icon, animateIconToEnd)
-
- if (iconOnly && showLabels) {
- Text(
- label,
- maxLines = 2,
- color = colors.label,
- overflow = TextOverflow.Ellipsis,
- textAlign = TextAlign.Center,
- )
- }
- }
-
- if (!iconOnly) {
- Column(verticalArrangement = Arrangement.Center, modifier = Modifier.fillMaxHeight()) {
- Text(
- label,
- color = colors.label,
- modifier = Modifier.basicMarquee(),
- )
- if (!TextUtils.isEmpty(secondaryLabel)) {
- Text(
- secondaryLabel ?: "",
- color = colors.secondaryLabel,
- modifier = Modifier.basicMarquee(),
- )
- }
- }
- }
-}
-
-@Composable
fun tileHeight(iconWithLabel: Boolean = false): Dp {
return if (iconWithLabel) {
TileDefaults.IconTileWithLabelHeight
@@ -441,20 +488,23 @@
private data class TileColors(
val background: Color,
+ val iconBackground: Color,
val label: Color,
val secondaryLabel: Color,
val icon: Color,
)
private object TileDefaults {
- val IconTileWithLabelHeight = 100.dp
+ val TileShape = CircleShape
+ val IconTileWithLabelHeight = 140.dp
@Composable
fun activeTileColors(): TileColors =
TileColors(
- background = MaterialTheme.colorScheme.primary,
- label = MaterialTheme.colorScheme.onPrimary,
- secondaryLabel = MaterialTheme.colorScheme.onPrimary,
+ background = MaterialTheme.colorScheme.surfaceVariant,
+ iconBackground = MaterialTheme.colorScheme.primary,
+ label = MaterialTheme.colorScheme.onSurfaceVariant,
+ secondaryLabel = MaterialTheme.colorScheme.onSurfaceVariant,
icon = MaterialTheme.colorScheme.onPrimary,
)
@@ -462,6 +512,7 @@
fun inactiveTileColors(): TileColors =
TileColors(
background = MaterialTheme.colorScheme.surfaceVariant,
+ iconBackground = MaterialTheme.colorScheme.surfaceVariant,
label = MaterialTheme.colorScheme.onSurfaceVariant,
secondaryLabel = MaterialTheme.colorScheme.onSurfaceVariant,
icon = MaterialTheme.colorScheme.onSurfaceVariant,
@@ -471,6 +522,7 @@
fun unavailableTileColors(): TileColors =
TileColors(
background = MaterialTheme.colorScheme.surface,
+ iconBackground = MaterialTheme.colorScheme.surface,
label = MaterialTheme.colorScheme.onSurface,
secondaryLabel = MaterialTheme.colorScheme.onSurface,
icon = MaterialTheme.colorScheme.onSurface,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt
index a6cfa75..7505b90 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt
@@ -48,6 +48,10 @@
tile.longClick(expandable)
}
+ fun onSecondaryClick(expandable: Expandable?) {
+ tile.secondaryClick(expandable)
+ }
+
fun startListening(token: Any) = tile.setListening(token, true)
fun stopListening(token: Any) = tile.setListening(token, false)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 1143c30..b1b67cf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -19,6 +19,7 @@
import android.animation.ArgbEvaluator
import android.animation.PropertyValuesHolder
import android.animation.ValueAnimator
+import android.annotation.SuppressLint
import android.content.Context
import android.content.res.ColorStateList
import android.content.res.Configuration
@@ -37,27 +38,31 @@
import android.util.TypedValue
import android.view.Gravity
import android.view.LayoutInflater
+import android.view.MotionEvent
import android.view.View
+import android.view.ViewConfiguration
import android.view.ViewGroup
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo
+import android.view.animation.AccelerateDecelerateInterpolator
import android.widget.Button
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.Switch
import android.widget.TextView
import androidx.annotation.VisibleForTesting
+import androidx.core.animation.doOnCancel
+import androidx.core.animation.doOnEnd
+import androidx.core.animation.doOnStart
import androidx.core.graphics.drawable.updateBounds
import com.android.app.tracing.traceSection
import com.android.settingslib.Utils
import com.android.systemui.Flags
-import com.android.systemui.Flags.quickSettingsVisualHapticsLongpress
import com.android.systemui.FontSizeUtils
import com.android.systemui.animation.Expandable
import com.android.systemui.animation.LaunchableView
import com.android.systemui.animation.LaunchableViewDelegate
import com.android.systemui.haptics.qs.QSLongPressEffect
-import com.android.systemui.haptics.qs.QSLongPressEffectViewBinder
import com.android.systemui.plugins.qs.QSIconView
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.qs.QSTile.AdapterState
@@ -65,11 +70,13 @@
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSIconViewImpl.QS_ANIM_LENGTH
import com.android.systemui.res.R
-import kotlinx.coroutines.DisposableHandle
import java.util.Objects
private const val TAG = "QSTileViewImpl"
-open class QSTileViewImpl @JvmOverloads constructor(
+
+open class QSTileViewImpl
+@JvmOverloads
+constructor(
context: Context,
private val collapsed: Boolean = false,
private val longPressEffect: QSLongPressEffect? = null,
@@ -83,12 +90,9 @@
private const val CHEVRON_NAME = "chevron"
private const val OVERLAY_NAME = "overlay"
const val UNAVAILABLE_ALPHA = 0.3f
- @VisibleForTesting
- internal const val TILE_STATE_RES_PREFIX = "tile_states_"
- @VisibleForTesting
- internal const val LONG_PRESS_EFFECT_WIDTH_SCALE = 1.1f
- @VisibleForTesting
- internal const val LONG_PRESS_EFFECT_HEIGHT_SCALE = 1.2f
+ @VisibleForTesting internal const val TILE_STATE_RES_PREFIX = "tile_states_"
+ @VisibleForTesting internal const val LONG_PRESS_EFFECT_WIDTH_SCALE = 1.1f
+ @VisibleForTesting internal const val LONG_PRESS_EFFECT_HEIGHT_SCALE = 1.2f
}
private val icon: QSIconViewImpl = QSIconViewImpl(context)
@@ -116,22 +120,25 @@
private val colorInactive = Utils.getColorAttrDefaultColor(context, R.attr.shadeInactive)
private val colorUnavailable = Utils.getColorAttrDefaultColor(context, R.attr.shadeDisabled)
- private val overlayColorActive = Utils.applyAlpha(
- /* alpha= */ 0.11f,
- Utils.getColorAttrDefaultColor(context, R.attr.onShadeActive))
- private val overlayColorInactive = Utils.applyAlpha(
- /* alpha= */ 0.08f,
- Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactive))
+ private val overlayColorActive =
+ Utils.applyAlpha(
+ /* alpha= */ 0.11f,
+ Utils.getColorAttrDefaultColor(context, R.attr.onShadeActive)
+ )
+ private val overlayColorInactive =
+ Utils.applyAlpha(
+ /* alpha= */ 0.08f,
+ Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactive)
+ )
private val colorLabelActive = Utils.getColorAttrDefaultColor(context, R.attr.onShadeActive)
private val colorLabelInactive = Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactive)
- private val colorLabelUnavailable =
- Utils.getColorAttrDefaultColor(context, R.attr.outline)
+ private val colorLabelUnavailable = Utils.getColorAttrDefaultColor(context, R.attr.outline)
private val colorSecondaryLabelActive =
Utils.getColorAttrDefaultColor(context, R.attr.onShadeActiveVariant)
private val colorSecondaryLabelInactive =
- Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactiveVariant)
+ Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactiveVariant)
private val colorSecondaryLabelUnavailable =
Utils.getColorAttrDefaultColor(context, R.attr.outline)
@@ -143,9 +150,7 @@
private lateinit var chevronView: ImageView
private var mQsLogger: QSLogger? = null
- /**
- * Controls if tile background is set to a [RippleDrawable] see [setClickable]
- */
+ /** Controls if tile background is set to a [RippleDrawable] see [setClickable] */
protected var showRippleEffect = true
private lateinit var qsTileBackground: RippleDrawable
@@ -157,20 +162,21 @@
private var backgroundColor: Int = 0
private var backgroundOverlayColor: Int = 0
- private val singleAnimator: ValueAnimator = ValueAnimator().apply {
- setDuration(QS_ANIM_LENGTH)
- addUpdateListener { animation ->
- setAllColors(
- // These casts will throw an exception if some property is missing. We should
- // always have all properties.
- animation.getAnimatedValue(BACKGROUND_NAME) as Int,
- animation.getAnimatedValue(LABEL_NAME) as Int,
- animation.getAnimatedValue(SECONDARY_LABEL_NAME) as Int,
- animation.getAnimatedValue(CHEVRON_NAME) as Int,
- animation.getAnimatedValue(OVERLAY_NAME) as Int,
- )
+ private val singleAnimator: ValueAnimator =
+ ValueAnimator().apply {
+ setDuration(QS_ANIM_LENGTH)
+ addUpdateListener { animation ->
+ setAllColors(
+ // These casts will throw an exception if some property is missing. We should
+ // always have all properties.
+ animation.getAnimatedValue(BACKGROUND_NAME) as Int,
+ animation.getAnimatedValue(LABEL_NAME) as Int,
+ animation.getAnimatedValue(SECONDARY_LABEL_NAME) as Int,
+ animation.getAnimatedValue(CHEVRON_NAME) as Int,
+ animation.getAnimatedValue(OVERLAY_NAME) as Int,
+ )
+ }
}
- }
private var accessibilityClass: String? = null
private var stateDescriptionDeltas: CharSequence? = null
@@ -178,32 +184,37 @@
private var tileState = false
private var lastState = INVALID
private var lastIconTint = 0
- private val launchableViewDelegate = LaunchableViewDelegate(
- this,
- superSetVisibility = { super.setVisibility(it) },
- )
+ private val launchableViewDelegate =
+ LaunchableViewDelegate(
+ this,
+ superSetVisibility = { super.setVisibility(it) },
+ )
private var lastDisabledByPolicy = false
private val locInScreen = IntArray(2)
/** Visuo-haptic long-press effects */
+ private var longPressEffectAnimator: ValueAnimator? = null
var haveLongPressPropertiesBeenReset = true
private set
+
private var paddingForLaunch = Rect()
private var initialLongPressProperties: QSLongPressProperties? = null
private var finalLongPressProperties: QSLongPressProperties? = null
private val colorEvaluator = ArgbEvaluator.getInstance()
val isLongPressEffectInitialized: Boolean
get() = longPressEffect?.hasInitialized == true
- private var longPressEffectHandle: DisposableHandle? = null
- val isLongPressEffectBound: Boolean
- get() = longPressEffectHandle != null
+
+ val areLongPressEffectPropertiesSet: Boolean
+ get() = initialLongPressProperties != null && finalLongPressProperties != null
init {
val typedValue = TypedValue()
if (!getContext().theme.resolveAttribute(R.attr.isQsTheme, typedValue, true)) {
- throw IllegalStateException("QSViewImpl must be inflated with a theme that contains " +
- "Theme.SystemUI.QuickSettings")
+ throw IllegalStateException(
+ "QSViewImpl must be inflated with a theme that contains " +
+ "Theme.SystemUI.QuickSettings"
+ )
}
setId(generateViewId())
orientation = LinearLayout.HORIZONTAL
@@ -261,13 +272,9 @@
setPaddingRelative(startPadding, padding, padding, padding)
val labelMargin = resources.getDimensionPixelSize(R.dimen.qs_label_container_margin)
- (labelContainer.layoutParams as MarginLayoutParams).apply {
- marginStart = labelMargin
- }
+ (labelContainer.layoutParams as MarginLayoutParams).apply { marginStart = labelMargin }
- (sideView.layoutParams as MarginLayoutParams).apply {
- marginStart = labelMargin
- }
+ (sideView.layoutParams as MarginLayoutParams).apply { marginStart = labelMargin }
(chevronView.layoutParams as MarginLayoutParams).apply {
height = iconSize
width = iconSize
@@ -285,8 +292,9 @@
}
private fun createAndAddLabels() {
- labelContainer = LayoutInflater.from(context)
- .inflate(R.layout.qs_tile_label, this, false) as IgnorableChildLinearLayout
+ labelContainer =
+ LayoutInflater.from(context).inflate(R.layout.qs_tile_label, this, false)
+ as IgnorableChildLinearLayout
label = labelContainer.requireViewById(R.id.tile_label)
secondaryLabel = labelContainer.requireViewById(R.id.app_label)
if (collapsed) {
@@ -304,8 +312,9 @@
}
private fun createAndAddSideView() {
- sideView = LayoutInflater.from(context)
- .inflate(R.layout.qs_tile_side_icon, this, false) as ViewGroup
+ sideView =
+ LayoutInflater.from(context).inflate(R.layout.qs_tile_side_icon, this, false)
+ as ViewGroup
customDrawableView = sideView.requireViewById(R.id.customDrawable)
chevronView = sideView.requireViewById(R.id.chevron)
setChevronColor(getChevronColorForState(QSTile.State.DEFAULT_STATE))
@@ -313,11 +322,12 @@
}
private fun createTileBackground(): Drawable {
- qsTileBackground = if (Flags.qsTileFocusState()) {
- mContext.getDrawable(R.drawable.qs_tile_background_flagged) as RippleDrawable
- } else {
- mContext.getDrawable(R.drawable.qs_tile_background) as RippleDrawable
- }
+ qsTileBackground =
+ if (Flags.qsTileFocusState()) {
+ mContext.getDrawable(R.drawable.qs_tile_background_flagged) as RippleDrawable
+ } else {
+ mContext.getDrawable(R.drawable.qs_tile_background) as RippleDrawable
+ }
qsTileFocusBackground = mContext.getDrawable(R.drawable.qs_tile_focused_background)!!
backgroundDrawable =
qsTileBackground.findDrawableByLayerId(R.id.background) as LayerDrawable
@@ -332,21 +342,21 @@
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
super.onLayout(changed, l, t, r, b)
updateHeight()
- maybeUpdateLongPressEffectDimensions()
+ maybeUpdateLongPressEffectWidth(measuredWidth.toFloat())
}
- private fun maybeUpdateLongPressEffectDimensions() {
+ private fun maybeUpdateLongPressEffectWidth(width: Float) {
if (!isLongClickable || longPressEffect == null) return
- val actualHeight = if (heightOverride != HeightOverrideable.NO_OVERRIDE) {
- heightOverride
- } else {
- measuredHeight
- }
- initialLongPressProperties?.height = actualHeight.toFloat()
- initialLongPressProperties?.width = measuredWidth.toFloat()
- finalLongPressProperties?.height = LONG_PRESS_EFFECT_HEIGHT_SCALE * actualHeight
- finalLongPressProperties?.width = LONG_PRESS_EFFECT_WIDTH_SCALE * measuredWidth
+ initialLongPressProperties?.width = width
+ finalLongPressProperties?.width = LONG_PRESS_EFFECT_WIDTH_SCALE * width
+ }
+
+ private fun maybeUpdateLongPressEffectHeight(height: Float) {
+ if (!isLongClickable || longPressEffect == null) return
+
+ initialLongPressProperties?.height = height
+ finalLongPressProperties?.height = LONG_PRESS_EFFECT_HEIGHT_SCALE * height
}
override fun onFocusChanged(gainFocus: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
@@ -360,6 +370,7 @@
}
}
}
+
private fun updateHeight() {
// TODO(b/332900989): Find a more robust way of resetting the tile if not reset by the
// launch animation.
@@ -368,16 +379,18 @@
// we must do it here
resetLongPressEffectProperties()
}
- val actualHeight = if (heightOverride != HeightOverrideable.NO_OVERRIDE) {
- heightOverride
- } else {
- measuredHeight
- }
+ val actualHeight =
+ if (heightOverride != HeightOverrideable.NO_OVERRIDE) {
+ heightOverride
+ } else {
+ measuredHeight
+ }
// Limit how much we affect the height, so we don't have rounding artifacts when the tile
// is too short.
val constrainedSquishiness = constrainSquishiness(squishinessFraction)
bottom = top + (actualHeight * constrainedSquishiness).toInt()
scrollY = (actualHeight - height) / 2
+ maybeUpdateLongPressEffectHeight(actualHeight.toFloat())
}
override fun updateAccessibilityOrder(previousView: View?): View {
@@ -395,22 +408,77 @@
override fun init(tile: QSTile) {
val expandable = Expandable.fromView(this)
- init(
+ if (longPressEffect != null) {
+ isHapticFeedbackEnabled = false
+ longPressEffect.qsTile = tile
+ longPressEffect.expandable = expandable
+ initLongPressEffectCallback()
+ init(
+ { _: View -> longPressEffect.onTileClick() },
+ null, // Haptics and long-clicks will be handled by the [QSLongPressEffect]
+ )
+ } else {
+ init(
{ _: View? -> tile.click(expandable) },
{ _: View? ->
tile.longClick(expandable)
true
- }
- )
- if (quickSettingsVisualHapticsLongpress()) {
- isHapticFeedbackEnabled = false // Haptics will be handled by the [QSLongPressEffect]
+ },
+ )
}
}
- private fun init(
- click: OnClickListener?,
- longClick: OnLongClickListener?
- ) {
+ private fun initLongPressEffectCallback() {
+ longPressEffect?.callback =
+ object : QSLongPressEffect.Callback {
+
+ override fun onPrepareForLaunch() {
+ prepareForLaunch()
+ }
+
+ override fun onResetProperties() {
+ resetLongPressEffectProperties()
+ }
+
+ override fun onStartAnimator() {
+ if (longPressEffectAnimator?.isRunning != true) {
+ longPressEffectAnimator =
+ ValueAnimator.ofFloat(0f, 1f).apply {
+ this.duration = longPressEffect?.effectDuration?.toLong() ?: 0L
+ interpolator = AccelerateDecelerateInterpolator()
+
+ doOnStart { longPressEffect?.handleAnimationStart() }
+ addUpdateListener {
+ val value = animatedValue as Float
+ if (value == 0f) {
+ bringToFront()
+ } else {
+ updateLongPressEffectProperties(value)
+ }
+ }
+ doOnEnd { longPressEffect?.handleAnimationComplete() }
+ doOnCancel { longPressEffect?.handleAnimationCancel() }
+ start()
+ }
+ }
+ }
+
+ override fun onReverseAnimator() {
+ longPressEffectAnimator?.let {
+ val pausedProgress = it.animatedFraction
+ longPressEffect?.playReverseHaptics(pausedProgress)
+ it.reverse()
+ }
+ }
+
+ override fun onCancelAnimator() {
+ resetLongPressEffectProperties()
+ longPressEffectAnimator?.cancel()
+ }
+ }
+ }
+
+ private fun init(click: OnClickListener?, longClick: OnLongClickListener?) {
setOnClickListener(click)
onLongClickListener = longClick
}
@@ -438,16 +506,18 @@
override fun setClickable(clickable: Boolean) {
super.setClickable(clickable)
- if (!Flags.qsTileFocusState()){
- background = if (clickable && showRippleEffect) {
- qsTileBackground.also {
- // In case that the colorBackgroundDrawable was used as the background, make sure
- // it has the correct callback instead of null
- backgroundDrawable.callback = it
+ if (!Flags.qsTileFocusState()) {
+ background =
+ if (clickable && showRippleEffect) {
+ qsTileBackground.also {
+ // In case that the colorBackgroundDrawable was used as the background, make
+ // sure
+ // it has the correct callback instead of null
+ backgroundDrawable.callback = it
+ }
+ } else {
+ backgroundDrawable
}
- } else {
- backgroundDrawable
- }
}
}
@@ -482,8 +552,10 @@
if (!TextUtils.isEmpty(accessibilityClass)) {
event.className = accessibilityClass
}
- if (event.contentChangeTypes == AccessibilityEvent.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION &&
- stateDescriptionDeltas != null) {
+ if (
+ event.contentChangeTypes == AccessibilityEvent.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION &&
+ stateDescriptionDeltas != null
+ ) {
event.text.add(stateDescriptionDeltas)
stateDescriptionDeltas = null
}
@@ -493,36 +565,39 @@
super.onInitializeAccessibilityNodeInfo(info)
// Clear selected state so it is not announce by talkback.
info.isSelected = false
- info.text = if (TextUtils.isEmpty(secondaryLabel.text)) {
- "${label.text}"
- } else {
- "${label.text}, ${secondaryLabel.text}"
- }
+ info.text =
+ if (TextUtils.isEmpty(secondaryLabel.text)) {
+ "${label.text}"
+ } else {
+ "${label.text}, ${secondaryLabel.text}"
+ }
if (lastDisabledByPolicy) {
info.addAction(
- AccessibilityNodeInfo.AccessibilityAction(
- AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.id,
- resources.getString(
- R.string.accessibility_tile_disabled_by_policy_action_description
- )
+ AccessibilityNodeInfo.AccessibilityAction(
+ AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.id,
+ resources.getString(
+ R.string.accessibility_tile_disabled_by_policy_action_description
)
+ )
)
}
if (!TextUtils.isEmpty(accessibilityClass)) {
- info.className = if (lastDisabledByPolicy) {
- Button::class.java.name
- } else {
- accessibilityClass
- }
+ info.className =
+ if (lastDisabledByPolicy) {
+ Button::class.java.name
+ } else {
+ accessibilityClass
+ }
if (Switch::class.java.name == accessibilityClass) {
info.isChecked = tileState
info.isCheckable = true
if (isLongClickable) {
info.addAction(
- AccessibilityNodeInfo.AccessibilityAction(
- AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.id,
- resources.getString(
- R.string.accessibility_long_click_tile)))
+ AccessibilityNodeInfo.AccessibilityAction(
+ AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.id,
+ resources.getString(R.string.accessibility_long_click_tile)
+ )
+ )
}
}
}
@@ -541,6 +616,28 @@
return sb.toString()
}
+ @SuppressLint("ClickableViewAccessibility")
+ override fun onTouchEvent(event: MotionEvent?): Boolean {
+ // let the View run the onTouch logic for click and long-click detection
+ val result = super.onTouchEvent(event)
+ if (longPressEffect != null) {
+ when (event?.actionMasked) {
+ MotionEvent.ACTION_DOWN -> {
+ longPressEffect.handleActionDown()
+ if (isLongClickable) {
+ postDelayed(
+ { longPressEffect.handleTimeoutComplete() },
+ ViewConfiguration.getTapTimeout().toLong(),
+ )
+ }
+ }
+ MotionEvent.ACTION_UP -> longPressEffect.handleActionUp()
+ MotionEvent.ACTION_CANCEL -> longPressEffect.handleActionCancel()
+ }
+ }
+ return result
+ }
+
// HANDLE STATE CHANGES RELATED METHODS
protected open fun handleStateChanged(state: QSTile.State) {
@@ -565,8 +662,11 @@
if (!TextUtils.isEmpty(state.stateDescription)) {
stateDescription.append(", ")
stateDescription.append(state.stateDescription)
- if (lastState != INVALID && state.state == lastState &&
- state.stateDescription != lastStateDescription) {
+ if (
+ lastState != INVALID &&
+ state.state == lastState &&
+ state.stateDescription != lastStateDescription
+ ) {
stateDescriptionDeltas = state.stateDescription
}
}
@@ -574,11 +674,12 @@
setStateDescription(stateDescription.toString())
lastStateDescription = state.stateDescription
- accessibilityClass = if (state.state == Tile.STATE_UNAVAILABLE) {
- null
- } else {
- state.expandedAccessibilityClassName
- }
+ accessibilityClass =
+ if (state.state == Tile.STATE_UNAVAILABLE) {
+ null
+ } else {
+ state.expandedAccessibilityClassName
+ }
if (state is AdapterState) {
val newState = state.value
@@ -593,49 +694,51 @@
}
if (!Objects.equals(secondaryLabel.text, state.secondaryLabel)) {
secondaryLabel.text = state.secondaryLabel
- secondaryLabel.visibility = if (TextUtils.isEmpty(state.secondaryLabel)) {
- GONE
- } else {
- VISIBLE
- }
+ secondaryLabel.visibility =
+ if (TextUtils.isEmpty(state.secondaryLabel)) {
+ GONE
+ } else {
+ VISIBLE
+ }
}
// Colors
if (state.state != lastState || state.disabledByPolicy != lastDisabledByPolicy) {
singleAnimator.cancel()
mQsLogger?.logTileBackgroundColorUpdateIfInternetTile(
- state.spec,
- state.state,
- state.disabledByPolicy,
- getBackgroundColorForState(state.state, state.disabledByPolicy))
+ state.spec,
+ state.state,
+ state.disabledByPolicy,
+ getBackgroundColorForState(state.state, state.disabledByPolicy)
+ )
if (allowAnimations) {
singleAnimator.setValues(
- colorValuesHolder(
- BACKGROUND_NAME,
- backgroundColor,
- getBackgroundColorForState(state.state, state.disabledByPolicy)
- ),
- colorValuesHolder(
- LABEL_NAME,
- label.currentTextColor,
- getLabelColorForState(state.state, state.disabledByPolicy)
- ),
- colorValuesHolder(
- SECONDARY_LABEL_NAME,
- secondaryLabel.currentTextColor,
- getSecondaryLabelColorForState(state.state, state.disabledByPolicy)
- ),
- colorValuesHolder(
- CHEVRON_NAME,
- chevronView.imageTintList?.defaultColor ?: 0,
- getChevronColorForState(state.state, state.disabledByPolicy)
- ),
- colorValuesHolder(
- OVERLAY_NAME,
- backgroundOverlayColor,
- getOverlayColorForState(state.state)
- )
+ colorValuesHolder(
+ BACKGROUND_NAME,
+ backgroundColor,
+ getBackgroundColorForState(state.state, state.disabledByPolicy)
+ ),
+ colorValuesHolder(
+ LABEL_NAME,
+ label.currentTextColor,
+ getLabelColorForState(state.state, state.disabledByPolicy)
+ ),
+ colorValuesHolder(
+ SECONDARY_LABEL_NAME,
+ secondaryLabel.currentTextColor,
+ getSecondaryLabelColorForState(state.state, state.disabledByPolicy)
+ ),
+ colorValuesHolder(
+ CHEVRON_NAME,
+ chevronView.imageTintList?.defaultColor ?: 0,
+ getChevronColorForState(state.state, state.disabledByPolicy)
+ ),
+ colorValuesHolder(
+ OVERLAY_NAME,
+ backgroundOverlayColor,
+ getOverlayColorForState(state.state)
)
+ )
singleAnimator.start()
} else {
setAllColors(
@@ -658,25 +761,16 @@
lastIconTint = icon.getColor(state)
// Long-press effects
- if (state.handlesLongClick &&
- longPressEffect?.initializeEffect(longPressEffectDuration) == true) {
- // bind the long-press effect and set it as the touch listener
- if (!isLongPressEffectBound) {
- longPressEffectHandle =
- QSLongPressEffectViewBinder.bind(
- this,
- longPressEffect,
- state.spec,
- )
- }
+ if (
+ state.handlesLongClick &&
+ longPressEffect?.initializeEffect(longPressEffectDuration) == true
+ ) {
showRippleEffect = false
initializeLongPressProperties(measuredHeight, measuredWidth)
} else {
// Long-press effects might have been enabled before but the new state does not
// handle a long-press. In this case, we go back to the behaviour of a regular tile
// and clean-up the resources
- setOnTouchListener(null)
- unbindLongPressEffect()
showRippleEffect = isClickable
initialLongPressProperties = null
finalLongPressProperties = null
@@ -791,7 +885,7 @@
}
private fun getChevronColorForState(state: Int, disabledByPolicy: Boolean = false): Int =
- getSecondaryLabelColorForState(state, disabledByPolicy)
+ getSecondaryLabelColorForState(state, disabledByPolicy)
private fun getOverlayColorForState(state: Int): Int {
return when (state) {
@@ -828,16 +922,18 @@
// Dimensions change
val newHeight =
interpolateFloat(
- effectProgress,
- initialLongPressProperties?.height ?: 0f,
- finalLongPressProperties?.height ?: 0f,
- ).toInt()
+ effectProgress,
+ initialLongPressProperties?.height ?: 0f,
+ finalLongPressProperties?.height ?: 0f,
+ )
+ .toInt()
val newWidth =
interpolateFloat(
- effectProgress,
- initialLongPressProperties?.width ?: 0f,
- finalLongPressProperties?.width ?: 0f,
- ).toInt()
+ effectProgress,
+ initialLongPressProperties?.width ?: 0f,
+ finalLongPressProperties?.width ?: 0f,
+ )
+ .toInt()
val startingHeight = initialLongPressProperties?.height?.toInt() ?: 0
val startingWidth = initialLongPressProperties?.width?.toInt() ?: 0
@@ -898,11 +994,6 @@
)
}
- private fun unbindLongPressEffect() {
- longPressEffectHandle?.dispose()
- longPressEffectHandle = null
- }
-
private fun interpolateFloat(fraction: Float, start: Float, end: Float): Float =
start + fraction * (end - start)
@@ -964,12 +1055,13 @@
}
@VisibleForTesting
- internal fun getCurrentColors(): List<Int> = listOf(
+ internal fun getCurrentColors(): List<Int> =
+ listOf(
backgroundColor,
label.currentTextColor,
secondaryLabel.currentTextColor,
chevronView.imageTintList?.defaultColor ?: 0
- )
+ )
inner class StateChangeRunnable(private val state: QSTile.State) : Runnable {
override fun run() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt
index 79720c1..5637115 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt
@@ -35,6 +35,7 @@
import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import com.android.systemui.screenrecord.RecordingController
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
+import com.android.systemui.screenrecord.data.repository.ScreenRecordRepository
import com.android.systemui.statusbar.phone.KeyguardDismissUtil
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
@@ -47,6 +48,7 @@
@Application private val context: Context,
@Main private val mainContext: CoroutineContext,
@Background private val backgroundContext: CoroutineContext,
+ private val screenRecordRepository: ScreenRecordRepository,
private val recordingController: RecordingController,
private val keyguardInteractor: KeyguardInteractor,
private val keyguardDismissUtil: KeyguardDismissUtil,
@@ -65,8 +67,7 @@
Log.d(TAG, "Cancelling countdown")
withContext(backgroundContext) { recordingController.cancelCountdown() }
}
- is ScreenRecordModel.Recording ->
- withContext(backgroundContext) { recordingController.stopRecording() }
+ is ScreenRecordModel.Recording -> screenRecordRepository.stopRecording()
is ScreenRecordModel.DoingNothing ->
withContext(mainContext) {
showPrompt(action.expandable, user.identifier)
@@ -122,8 +123,7 @@
controller,
animateBackgroundBoundsChange = true,
)
- }
- ?: dialog.show()
+ } ?: dialog.show()
} else {
dialog.show()
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 0327ec7..23faf7d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -73,7 +73,6 @@
import androidx.annotation.NonNull;
-import com.android.compose.animation.scene.SceneKey;
import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.AssistUtils;
@@ -99,12 +98,10 @@
import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
import com.android.systemui.scene.domain.interactor.SceneInteractor;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
-import com.android.systemui.scene.shared.model.Scenes;
+import com.android.systemui.scene.shared.model.SceneFamilies;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeViewController;
-import com.android.systemui.shade.domain.interactor.ShadeInteractor;
-import com.android.systemui.shade.shared.model.ShadeMode;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.QuickStepContract;
@@ -158,7 +155,6 @@
private final ScreenPinningRequest mScreenPinningRequest;
private final NotificationShadeWindowController mStatusBarWinController;
private final Provider<SceneInteractor> mSceneInteractor;
- private final Provider<ShadeInteractor> mShadeInteractor;
private final Runnable mConnectionRunnable = () ->
internalConnectToCurrentUser("runnable: startConnectionToCurrentUser");
@@ -247,7 +243,7 @@
// Gesture was too short to be picked up by scene container touch
// handling; programmatically start the transition to shade scene.
mSceneInteractor.get().changeScene(
- getShadeSceneKey(),
+ SceneFamilies.NotifShade,
"short launcher swipe"
);
}
@@ -267,7 +263,7 @@
"trackpad swipe");
} else if (action == ACTION_UP) {
mSceneInteractor.get().changeScene(
- getShadeSceneKey(),
+ SceneFamilies.NotifShade,
"short trackpad swipe"
);
}
@@ -632,7 +628,6 @@
NotificationShadeWindowController statusBarWinController,
SysUiState sysUiState,
Provider<SceneInteractor> sceneInteractor,
- Provider<ShadeInteractor> shadeInteractor,
UserTracker userTracker,
WakefulnessLifecycle wakefulnessLifecycle,
UiEventLogger uiEventLogger,
@@ -659,7 +654,6 @@
mScreenPinningRequest = screenPinningRequest;
mStatusBarWinController = statusBarWinController;
mSceneInteractor = sceneInteractor;
- mShadeInteractor = shadeInteractor;
mUserTracker = userTracker;
mConnectionBackoffAttempts = 0;
mRecentsComponentName = ComponentName.unflattenFromString(context.getString(
@@ -925,12 +919,6 @@
}
}
- private SceneKey getShadeSceneKey() {
- return mShadeInteractor.get().getShadeMode().getValue() == ShadeMode.dual()
- ? Scenes.NotificationsShade
- : Scenes.Shade;
- }
-
private void notifyHomeRotationEnabled(boolean enabled) {
for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
mConnectionCallbacks.get(i).onHomeRotationEnabled(enabled);
diff --git a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
index da23936..323ca87 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
@@ -20,6 +20,9 @@
import com.android.systemui.notifications.ui.composable.NotificationsShadeSessionModule
import com.android.systemui.scene.domain.SceneDomainModule
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
+import com.android.systemui.scene.domain.resolver.HomeSceneFamilyResolverModule
+import com.android.systemui.scene.domain.resolver.NotifShadeSceneFamilyResolverModule
+import com.android.systemui.scene.domain.resolver.QuickSettingsSceneFamilyResolverModule
import com.android.systemui.scene.domain.startable.SceneContainerStartable
import com.android.systemui.scene.domain.startable.ScrimStartable
import com.android.systemui.scene.shared.model.SceneContainerConfig
@@ -42,6 +45,11 @@
QuickSettingsSceneModule::class,
ShadeSceneModule::class,
SceneDomainModule::class,
+
+ // List SceneResolver modules for supported SceneFamilies
+ HomeSceneFamilyResolverModule::class,
+ NotifShadeSceneFamilyResolverModule::class,
+ QuickSettingsSceneFamilyResolverModule::class,
],
)
interface KeyguardlessSceneContainerFrameworkModule {
diff --git a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
index a0cf82a..4691eba 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
@@ -21,6 +21,9 @@
import com.android.systemui.notifications.ui.composable.NotificationsShadeSessionModule
import com.android.systemui.scene.domain.SceneDomainModule
import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
+import com.android.systemui.scene.domain.resolver.HomeSceneFamilyResolverModule
+import com.android.systemui.scene.domain.resolver.NotifShadeSceneFamilyResolverModule
+import com.android.systemui.scene.domain.resolver.QuickSettingsSceneFamilyResolverModule
import com.android.systemui.scene.domain.startable.SceneContainerStartable
import com.android.systemui.scene.domain.startable.ScrimStartable
import com.android.systemui.scene.shared.model.SceneContainerConfig
@@ -48,6 +51,11 @@
NotificationsShadeSceneModule::class,
NotificationsShadeSessionModule::class,
SceneDomainModule::class,
+
+ // List SceneResolver modules for supported SceneFamilies
+ HomeSceneFamilyResolverModule::class,
+ NotifShadeSceneFamilyResolverModule::class,
+ QuickSettingsSceneFamilyResolverModule::class,
],
)
interface SceneContainerFrameworkModule {
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt
index a326ec1..9a7eef8 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt
@@ -17,6 +17,7 @@
package com.android.systemui.scene
import com.android.systemui.scene.domain.SceneDomainModule
+import com.android.systemui.scene.domain.resolver.HomeSceneFamilyResolverModule
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.Scenes
import dagger.Module
@@ -31,6 +32,9 @@
GoneSceneModule::class,
LockscreenSceneModule::class,
SceneDomainModule::class,
+
+ // List SceneResolver modules for supported SceneFamilies
+ HomeSceneFamilyResolverModule::class,
],
)
object ShadelessSceneContainerFrameworkModule {
@@ -49,11 +53,12 @@
Scenes.Bouncer,
),
initialSceneKey = Scenes.Lockscreen,
- mapOf(
- Scenes.Gone to 0,
- Scenes.Lockscreen to 0,
- Scenes.Bouncer to 1,
- )
+ navigationDistances =
+ mapOf(
+ Scenes.Gone to 0,
+ Scenes.Lockscreen to 0,
+ Scenes.Bouncer to 1,
+ )
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/SceneDomainModule.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/SceneDomainModule.kt
index 9b2a6dd..be792df 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/SceneDomainModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/SceneDomainModule.kt
@@ -16,14 +16,12 @@
package com.android.systemui.scene.domain
-import com.android.systemui.scene.domain.resolver.HomeSceneFamilyResolverModule
import com.android.systemui.scene.domain.resolver.SceneResolverModule
import dagger.Module
@Module(
includes =
[
- HomeSceneFamilyResolverModule::class,
SceneResolverModule::class,
]
)
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 2f66d6b..c98a49b 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
@@ -36,7 +36,9 @@
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
@@ -156,6 +158,28 @@
)
/**
+ * The amount of transition into or out of the given [scene].
+ *
+ * The value will be `0` if not in this scene or `1` when fully in the given scene.
+ */
+ fun transitionProgress(scene: SceneKey): Flow<Float> {
+ return transitionState.flatMapLatest { transition ->
+ when (transition) {
+ is ObservableTransitionState.Idle -> {
+ flowOf(if (transition.currentScene == scene) 1f else 0f)
+ }
+ is ObservableTransitionState.Transition -> {
+ when {
+ transition.toScene == scene -> transition.progress
+ transition.fromScene == scene -> transition.progress.map { 1f - it }
+ else -> flowOf(0f)
+ }
+ }
+ }
+ }
+ }
+
+ /**
* Returns the keys of all scenes in the container.
*
* The scenes will be sorted in z-order such that the last one is the one that should be
@@ -217,7 +241,14 @@
loggingReason: String,
) {
val currentSceneKey = currentScene.value
- val resolvedScene = sceneFamilyResolvers.get()[toScene]?.resolvedScene?.value ?: toScene
+ val resolvedScene =
+ sceneFamilyResolvers.get()[toScene]?.let { familyResolver ->
+ if (familyResolver.includesScene(currentSceneKey)) {
+ return
+ } else {
+ familyResolver.resolvedScene.value
+ }
+ } ?: toScene
if (
!validateSceneChange(
from = currentSceneKey,
@@ -298,8 +329,9 @@
* Returns the [concrete scene][Scenes] for [sceneKey] if it is a [scene family][SceneFamilies],
* otherwise returns a singleton [Flow] containing [sceneKey].
*/
- fun resolveSceneFamily(sceneKey: SceneKey): Flow<SceneKey> =
- sceneFamilyResolvers.get()[sceneKey]?.resolvedScene ?: flowOf(sceneKey)
+ fun resolveSceneFamily(sceneKey: SceneKey): Flow<SceneKey> = flow {
+ emitAll(sceneFamilyResolvers.get()[sceneKey]?.resolvedScene ?: flowOf(sceneKey))
+ }
private fun isVisibleInternal(
raw: Boolean = repository.isVisible.value,
@@ -343,4 +375,12 @@
return from != to
}
+
+ /** Returns a flow indicating if the currently visible scene can be resolved from [family]. */
+ fun isCurrentSceneInFamily(family: SceneKey): Flow<Boolean> =
+ currentScene.map { currentScene -> isSceneInFamily(currentScene, family) }
+
+ /** Returns `true` if [scene] can be resolved from [family]. */
+ fun isSceneInFamily(scene: SceneKey, family: SceneKey): Boolean =
+ sceneFamilyResolvers.get()[family]?.includesScene(scene) == true
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt
index f19929c..9e91b66 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt
@@ -51,25 +51,42 @@
override val resolvedScene: StateFlow<SceneKey> =
combine(
deviceEntryInteractor.canSwipeToEnter,
+ deviceEntryInteractor.isDeviceEntered,
deviceEntryInteractor.isUnlocked,
transform = ::homeScene,
)
.stateIn(
scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
+ started = SharingStarted.Eagerly,
initialValue =
homeScene(
deviceEntryInteractor.canSwipeToEnter.value,
+ deviceEntryInteractor.isDeviceEntered.value,
deviceEntryInteractor.isUnlocked.value,
)
)
- private fun homeScene(canSwipeToEnter: Boolean?, isUnlocked: Boolean): SceneKey =
+ override fun includesScene(scene: SceneKey): Boolean = scene in homeScenes
+
+ private fun homeScene(
+ canSwipeToEnter: Boolean?,
+ isDeviceEntered: Boolean,
+ isUnlocked: Boolean,
+ ): SceneKey =
when {
canSwipeToEnter == true -> Scenes.Lockscreen
- isUnlocked -> Scenes.Gone
- else -> Scenes.Lockscreen
+ !isDeviceEntered -> Scenes.Lockscreen
+ !isUnlocked -> Scenes.Lockscreen
+ else -> Scenes.Gone
}
+
+ companion object {
+ val homeScenes =
+ setOf(
+ Scenes.Gone,
+ Scenes.Lockscreen,
+ )
+ }
}
@Module
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/NotifShadeSceneFamilyResolver.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/NotifShadeSceneFamilyResolver.kt
new file mode 100644
index 0000000..99e554e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/NotifShadeSceneFamilyResolver.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.scene.domain.resolver
+
+import com.android.compose.animation.scene.SceneKey
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.scene.shared.model.SceneFamilies
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+@SysUISingleton
+class NotifShadeSceneFamilyResolver
+@Inject
+constructor(
+ @Application applicationScope: CoroutineScope,
+ shadeInteractor: ShadeInteractor,
+) : SceneResolver {
+ override val targetFamily: SceneKey = SceneFamilies.NotifShade
+
+ override val resolvedScene: StateFlow<SceneKey> =
+ shadeInteractor.shadeMode
+ .map(::notifShadeScene)
+ .stateIn(
+ applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue = notifShadeScene(shadeInteractor.shadeMode.value),
+ )
+
+ override fun includesScene(scene: SceneKey): Boolean = scene in notifShadeScenes
+
+ private fun notifShadeScene(shadeMode: ShadeMode) =
+ when (shadeMode) {
+ is ShadeMode.Single -> Scenes.Shade
+ is ShadeMode.Dual -> Scenes.NotificationsShade
+ is ShadeMode.Split -> Scenes.Shade
+ }
+
+ companion object {
+ val notifShadeScenes =
+ setOf(
+ Scenes.NotificationsShade,
+ Scenes.Shade,
+ )
+ }
+}
+
+@Module
+interface NotifShadeSceneFamilyResolverModule {
+ @Binds @IntoSet fun bindResolver(interactor: NotifShadeSceneFamilyResolver): SceneResolver
+}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/QuickSettingsSceneFamilyResolver.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/QuickSettingsSceneFamilyResolver.kt
new file mode 100644
index 0000000..2962a3e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/QuickSettingsSceneFamilyResolver.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.scene.domain.resolver
+
+import com.android.compose.animation.scene.SceneKey
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.scene.shared.model.SceneFamilies
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+@SysUISingleton
+class QuickSettingsSceneFamilyResolver
+@Inject
+constructor(
+ @Application applicationScope: CoroutineScope,
+ shadeInteractor: ShadeInteractor,
+) : SceneResolver {
+ override val targetFamily: SceneKey = SceneFamilies.QuickSettings
+
+ override val resolvedScene: StateFlow<SceneKey> =
+ shadeInteractor.shadeMode
+ .map(::quickSettingsScene)
+ .stateIn(
+ applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue = quickSettingsScene(shadeInteractor.shadeMode.value),
+ )
+
+ override fun includesScene(scene: SceneKey): Boolean = scene in quickSettingsScenes
+
+ private fun quickSettingsScene(shadeMode: ShadeMode) =
+ when (shadeMode) {
+ is ShadeMode.Single -> Scenes.QuickSettings
+ is ShadeMode.Dual -> Scenes.QuickSettingsShade
+ is ShadeMode.Split -> Scenes.Shade
+ }
+
+ companion object {
+ val quickSettingsScenes =
+ setOf(
+ Scenes.QuickSettings,
+ Scenes.QuickSettingsShade,
+ Scenes.Shade,
+ )
+ }
+}
+
+@Module
+interface QuickSettingsSceneFamilyResolverModule {
+ @Binds @IntoSet fun bindResolver(interactor: QuickSettingsSceneFamilyResolver): SceneResolver
+}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/SceneResolver.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/SceneResolver.kt
index 8372529..8d7247b 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/SceneResolver.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/SceneResolver.kt
@@ -29,6 +29,9 @@
/** The concrete scene that [targetFamily] is currently resolved to. */
val resolvedScene: StateFlow<SceneKey>
+
+ /** Returns `true` if [scene] can be resolved from [targetFamily]. */
+ fun includesScene(scene: SceneKey): Boolean
}
@Module
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 0304e73..1e689bd 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -39,6 +39,7 @@
import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
import com.android.systemui.deviceentry.shared.model.DeviceUnlockSource
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor
import com.android.systemui.model.SceneContainerPlugin
import com.android.systemui.model.SysUiState
import com.android.systemui.model.updateFlags
@@ -81,6 +82,7 @@
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.filterNot
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
@@ -120,6 +122,7 @@
private val uiEventLogger: UiEventLogger,
private val sceneBackInteractor: SceneBackInteractor,
private val shadeSessionStorage: SessionStorage,
+ private val windowMgrLockscreenVisInteractor: WindowManagerLockscreenVisibilityInteractor,
) : CoreStartable {
private val centralSurfaces: CentralSurfaces?
get() = centralSurfacesOptLazy.get().getOrNull()
@@ -227,6 +230,25 @@
handleDeviceUnlockStatus()
handlePowerState()
handleShadeTouchability()
+ handleSurfaceBehindKeyguardVisibility()
+ }
+
+ private fun handleSurfaceBehindKeyguardVisibility() {
+ applicationScope.launch {
+ sceneInteractor.currentScene.collectLatest { currentScene ->
+ if (currentScene == Scenes.Lockscreen) {
+ // Wait for surface to become visible
+ windowMgrLockscreenVisInteractor.surfaceBehindVisibility.first { it }
+ // Make sure the device is actually unlocked before force-changing the scene
+ deviceUnlockedInteractor.deviceUnlockStatus.first { it.isUnlocked }
+ // Override the current transition, if any, by forcing the scene to Gone
+ sceneInteractor.changeScene(
+ toScene = Scenes.Gone,
+ loggingReason = "surface behind keyguard is visible",
+ )
+ }
+ }
+ }
}
private fun handleBouncerImeVisibility() {
@@ -329,8 +351,7 @@
Scenes.Gone to "device was unlocked in Bouncer scene"
} else {
val prevScene = previousScene.value
- (prevScene
- ?: Scenes.Gone) to
+ (prevScene ?: Scenes.Gone) to
"device was unlocked in Bouncer scene, from sceneKey=$prevScene"
}
isOnLockscreen ->
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModel.kt
index 99118bc..c34a6cd 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModel.kt
@@ -23,7 +23,7 @@
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.shared.model.ShadeMode
@@ -43,39 +43,42 @@
) {
val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
shadeInteractor.shadeMode
- .map { shadeMode -> destinationScenes(shadeMode = shadeMode) }
+ .map(::destinationScenes)
.stateIn(
scope = applicationScope,
started = SharingStarted.WhileSubscribed(),
- initialValue = destinationScenes(shadeMode = shadeInteractor.shadeMode.value)
+ initialValue =
+ destinationScenes(
+ shadeMode = shadeInteractor.shadeMode.value,
+ )
)
- private fun destinationScenes(shadeMode: ShadeMode): Map<UserAction, UserActionResult> {
+ private fun destinationScenes(
+ shadeMode: ShadeMode,
+ ): Map<UserAction, UserActionResult> {
return buildMap {
- if (shadeMode is ShadeMode.Single) {
- this[
+ if (
+ shadeMode is ShadeMode.Single ||
+ // TODO(b/338577208): Remove this once we add Dual Shade invocation zones.
+ shadeMode is ShadeMode.Dual
+ ) {
+ put(
Swipe(
pointerCount = 2,
fromSource = Edge.Top,
direction = SwipeDirection.Down,
- )] = UserActionResult(Scenes.QuickSettings)
+ ),
+ UserActionResult(SceneFamilies.QuickSettings)
+ )
}
- // TODO(b/338577208): Remove this once we add Dual Shade invocation zones.
- if (shadeMode is ShadeMode.Dual) {
- this[
- Swipe(
- pointerCount = 2,
- fromSource = Edge.Top,
- direction = SwipeDirection.Down,
- )] = UserActionResult(Scenes.QuickSettingsShade)
- }
-
- val downSceneKey =
- if (shadeMode is ShadeMode.Dual) Scenes.NotificationsShade else Scenes.Shade
- val downTransitionKey = ToSplitShade.takeIf { shadeMode is ShadeMode.Split }
- this[Swipe(direction = SwipeDirection.Down)] =
- UserActionResult(downSceneKey, downTransitionKey)
+ put(
+ Swipe(direction = SwipeDirection.Down),
+ UserActionResult(
+ SceneFamilies.NotifShade,
+ ToSplitShade.takeIf { shadeMode is ShadeMode.Split }
+ )
+ )
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepository.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepository.kt
index d59d220..9eeb3b9 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepository.kt
@@ -28,6 +28,7 @@
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.withContext
/**
* Repository storing information about the state of screen recording.
@@ -38,6 +39,9 @@
interface ScreenRecordRepository {
/** The current screen recording state. Note that this is a cold flow. */
val screenRecordState: Flow<ScreenRecordModel>
+
+ /** Stops the recording. */
+ suspend fun stopRecording()
}
@SysUISingleton
@@ -90,4 +94,8 @@
ScreenRecordModel.DoingNothing
}
}
+
+ override suspend fun stopRecording() {
+ withContext(bgCoroutineContext) { recordingController.stopRecording() }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt
index 14659e7..f463cb5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt
@@ -47,6 +47,7 @@
viewsIdToTranslate =
setOf(
ViewIdToTranslate(R.id.quick_settings_panel, START, filterShade),
+ ViewIdToTranslate(R.id.qs_footer_actions, START, filterShade),
ViewIdToTranslate(R.id.notification_stack_scroller, END, filterShade)),
progressProvider = progressProvider)
}
@@ -55,9 +56,8 @@
UnfoldConstantTranslateAnimator(
viewsIdToTranslate =
setOf(
- ViewIdToTranslate(R.id.statusIcons, END, filterShade),
+ ViewIdToTranslate(R.id.shade_header_system_icons, END, filterShade),
ViewIdToTranslate(R.id.privacy_container, END, filterShade),
- ViewIdToTranslate(R.id.batteryRemainingIcon, END, filterShade),
ViewIdToTranslate(R.id.carrier_group, END, filterShade),
ViewIdToTranslate(R.id.clock, START, filterShade),
ViewIdToTranslate(R.id.date, START, filterShade)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 3826b50..262befc 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -537,8 +537,6 @@
private final KeyguardMediaController mKeyguardMediaController;
private final Optional<KeyguardUnfoldTransition> mKeyguardUnfoldTransition;
- private final Optional<NotificationPanelUnfoldAnimationController>
- mNotificationPanelUnfoldAnimationController;
/** The drag distance required to fully expand the split shade. */
private int mSplitShadeFullTransitionDistance;
@@ -964,8 +962,6 @@
mKeyguardUnfoldTransition = unfoldComponent.map(
SysUIUnfoldComponent::getKeyguardUnfoldTransition);
- mNotificationPanelUnfoldAnimationController = unfoldComponent.map(
- SysUIUnfoldComponent::getNotificationPanelUnfoldAnimationController);
updateUserSwitcherFlags();
mKeyguardBottomAreaViewModel = keyguardBottomAreaViewModel;
@@ -1131,9 +1127,6 @@
mShadeHeaderController.init();
mShadeHeaderController.setShadeCollapseAction(
() -> collapse(/* delayed= */ false , /* speedUpFactor= */ 1.0f));
- mKeyguardUnfoldTransition.ifPresent(u -> u.setup(mView));
- mNotificationPanelUnfoldAnimationController.ifPresent(controller ->
- controller.setup(mNotificationContainerParent));
// Dreaming->Lockscreen
collectFlow(
@@ -1511,9 +1504,6 @@
if (!KeyguardBottomAreaRefactor.isEnabled()) {
setKeyguardBottomAreaVisibility(mBarState, false);
}
-
- mKeyguardUnfoldTransition.ifPresent(u -> u.setup(mView));
- mNotificationPanelUnfoldAnimationController.ifPresent(u -> u.setup(mView));
}
private void attachSplitShadeMediaPlayerContainer(FrameLayout container) {
@@ -1797,6 +1787,7 @@
private void updateKeyguardStatusViewAlignment(boolean animate) {
boolean shouldBeCentered = shouldKeyguardStatusViewBeCentered();
+ mKeyguardUnfoldTransition.ifPresent(t -> t.setStatusViewCentered(shouldBeCentered));
if (MigrateClocksToBlueprint.isEnabled()) {
mKeyguardInteractor.setClockShouldBeCentered(shouldBeCentered);
return;
@@ -1804,7 +1795,6 @@
ConstraintLayout layout = mNotificationContainerParent;
mKeyguardStatusViewController.updateAlignment(
layout, mSplitShadeEnabled, shouldBeCentered, animate);
- mKeyguardUnfoldTransition.ifPresent(t -> t.setStatusViewCentered(shouldBeCentered));
}
private boolean shouldKeyguardStatusViewBeCentered() {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index c01b7b6..e41f99b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -32,6 +32,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.AuthKeyguardMessageArea;
+import com.android.keyguard.KeyguardUnfoldTransition;
import com.android.keyguard.LockIconViewController;
import com.android.systemui.Dumpable;
import com.android.systemui.animation.ActivityTransitionAnimator;
@@ -72,6 +73,7 @@
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
+import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
import com.android.systemui.util.time.SystemClock;
@@ -174,6 +176,7 @@
DozeScrimController dozeScrimController,
NotificationShadeWindowController controller,
Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider,
+ Optional<SysUIUnfoldComponent> unfoldComponent,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
NotificationInsetsController notificationInsetsController,
AmbientState ambientState,
@@ -227,13 +230,21 @@
bouncerViewBinder.bind(mView.findViewById(R.id.keyguard_bouncer_container));
collectFlow(mView, keyguardTransitionInteractor.transition(
- Edge.Companion.create(LOCKSCREEN, DREAMING)),
+ Edge.create(LOCKSCREEN, DREAMING)),
mLockscreenToDreamingTransition);
collectFlow(
mView,
notificationLaunchAnimationInteractor.isLaunchAnimationRunning(),
this::setExpandAnimationRunning);
+ var keyguardUnfoldTransition = unfoldComponent.map(
+ SysUIUnfoldComponent::getKeyguardUnfoldTransition);
+ var notificationPanelUnfoldAnimationController = unfoldComponent.map(
+ SysUIUnfoldComponent::getNotificationPanelUnfoldAnimationController);
+
+ keyguardUnfoldTransition.ifPresent(KeyguardUnfoldTransition::setup);
+ notificationPanelUnfoldAnimationController.ifPresent(u -> u.setup(mView));
+
mClock = clock;
if (featureFlagsClassic.isEnabled(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION)) {
unfoldTransitionProgressProvider.ifPresent(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
index ac1f971..004db16 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
@@ -21,15 +21,12 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.dagger.ShadeTouchLog
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.TransitionKeys.SlightlyFasterShadeCollapse
import com.android.systemui.shade.ShadeController.ShadeVisibilityListener
import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.VibratorHelper
@@ -60,7 +57,6 @@
private val shadeInteractor: ShadeInteractor,
private val sceneInteractor: SceneInteractor,
private val notificationStackScrollLayout: NotificationStackScrollLayout,
- @ShadeTouchLog private val touchLog: LogBuffer,
private val vibratorHelper: VibratorHelper,
commandQueue: CommandQueue,
statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
@@ -176,19 +172,14 @@
}
override fun expandToNotifications() {
- val shadeMode = shadeInteractor.shadeMode.value
sceneInteractor.changeScene(
- if (shadeMode is ShadeMode.Dual) Scenes.NotificationsShade else Scenes.Shade,
- "ShadeController.animateExpandShade"
+ SceneFamilies.NotifShade,
+ "ShadeController.animateExpandShade",
)
}
override fun expandToQs() {
- val shadeMode = shadeInteractor.shadeMode.value
- sceneInteractor.changeScene(
- if (shadeMode is ShadeMode.Dual) Scenes.QuickSettingsShade else Scenes.QuickSettings,
- "ShadeController.animateExpandQs"
- )
+ sceneInteractor.changeScene(SceneFamilies.QuickSettings, "ShadeController.animateExpandQs")
}
override fun setVisibilityListener(listener: ShadeVisibilityListener) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
index fe16fc0..d5b4f4d 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
@@ -21,25 +21,23 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
+import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
/** ShadeInteractor implementation for Scene Container. */
-@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class ShadeInteractorSceneContainerImpl
@Inject
@@ -52,10 +50,11 @@
override val shadeMode: StateFlow<ShadeMode> = shadeRepository.shadeMode
override val shadeExpansion: StateFlow<Float> =
- sceneBasedExpansion(sceneInteractor, notificationsScene)
+ sceneBasedExpansion(sceneInteractor, SceneFamilies.NotifShade)
.stateIn(scope, SharingStarted.Eagerly, 0f)
- private val sceneBasedQsExpansion = sceneBasedExpansion(sceneInteractor, quickSettingsScene)
+ private val sceneBasedQsExpansion =
+ sceneBasedExpansion(sceneInteractor, SceneFamilies.QuickSettings)
override val qsExpansion: StateFlow<Float> =
combine(
@@ -78,23 +77,38 @@
.stateIn(scope, SharingStarted.Eagerly, false)
override val isQsBypassingShade: Flow<Boolean> =
- sceneInteractor.transitionState
- .map { state ->
- when (state) {
- is ObservableTransitionState.Idle -> false
- is ObservableTransitionState.Transition ->
- state.toScene == quickSettingsScene && state.fromScene != notificationsScene
- }
+ combine(
+ sceneInteractor.resolveSceneFamily(SceneFamilies.QuickSettings),
+ sceneInteractor.resolveSceneFamily(SceneFamilies.NotifShade),
+ ::Pair
+ )
+ .flatMapLatestConflated { (quickSettingsScene, notificationsScene) ->
+ sceneInteractor.transitionState
+ .map { state ->
+ when (state) {
+ is ObservableTransitionState.Idle -> false
+ is ObservableTransitionState.Transition ->
+ state.toScene == quickSettingsScene &&
+ state.fromScene != notificationsScene
+ }
+ }
+ .distinctUntilChanged()
}
.distinctUntilChanged()
override val isQsFullscreen: Flow<Boolean> =
- sceneInteractor.transitionState
- .map { state ->
- when (state) {
- is ObservableTransitionState.Idle -> state.currentScene == quickSettingsScene
- is ObservableTransitionState.Transition -> false
- }
+ sceneInteractor
+ .resolveSceneFamily(SceneFamilies.QuickSettings)
+ .flatMapLatestConflated { quickSettingsScene ->
+ sceneInteractor.transitionState
+ .map { state ->
+ when (state) {
+ is ObservableTransitionState.Idle ->
+ state.currentScene == quickSettingsScene
+ is ObservableTransitionState.Transition -> false
+ }
+ }
+ .distinctUntilChanged()
}
.distinctUntilChanged()
@@ -108,34 +122,39 @@
.stateIn(scope, SharingStarted.Eagerly, false)
override val isUserInteractingWithShade: Flow<Boolean> =
- sceneBasedInteracting(sceneInteractor, notificationsScene)
+ sceneBasedInteracting(sceneInteractor, SceneFamilies.NotifShade)
override val isUserInteractingWithQs: Flow<Boolean> =
- sceneBasedInteracting(sceneInteractor, quickSettingsScene)
+ sceneBasedInteracting(sceneInteractor, SceneFamilies.QuickSettings)
/**
* Returns a flow that uses scene transition progress to and from a scene that is pulled down
* from the top of the screen to a 0-1 expansion amount float.
*/
fun sceneBasedExpansion(sceneInteractor: SceneInteractor, sceneKey: SceneKey) =
- sceneInteractor.transitionState
- .flatMapLatest { state ->
- when (state) {
- is ObservableTransitionState.Idle ->
- if (state.currentScene == sceneKey) {
- flowOf(1f)
- } else {
- flowOf(0f)
+ sceneInteractor
+ .resolveSceneFamily(sceneKey)
+ .flatMapLatestConflated { resolvedSceneKey ->
+ sceneInteractor.transitionState
+ .flatMapLatestConflated { state ->
+ when (state) {
+ is ObservableTransitionState.Idle ->
+ if (state.currentScene == resolvedSceneKey) {
+ flowOf(1f)
+ } else {
+ flowOf(0f)
+ }
+ is ObservableTransitionState.Transition ->
+ if (state.toScene == resolvedSceneKey) {
+ state.progress
+ } else if (state.fromScene == resolvedSceneKey) {
+ state.progress.map { progress -> 1 - progress }
+ } else {
+ flowOf(0f)
+ }
}
- is ObservableTransitionState.Transition ->
- if (state.toScene == sceneKey) {
- state.progress
- } else if (state.fromScene == sceneKey) {
- state.progress.map { progress -> 1 - progress }
- } else {
- flowOf(0f)
- }
- }
+ }
+ .distinctUntilChanged()
}
.distinctUntilChanged()
@@ -145,29 +164,16 @@
*/
fun sceneBasedInteracting(sceneInteractor: SceneInteractor, sceneKey: SceneKey) =
sceneInteractor.transitionState
- .map { state ->
+ .flatMapLatestConflated { state ->
when (state) {
- is ObservableTransitionState.Idle -> false
+ is ObservableTransitionState.Idle -> flowOf(false)
is ObservableTransitionState.Transition ->
- state.isInitiatedByUserInput &&
- (state.toScene == sceneKey || state.fromScene == sceneKey)
+ sceneInteractor.resolveSceneFamily(sceneKey).map { resolvedSceneKey ->
+ state.isInitiatedByUserInput &&
+ (state.toScene == resolvedSceneKey ||
+ state.fromScene == resolvedSceneKey)
+ }
}
}
.distinctUntilChanged()
-
- private val notificationsScene: SceneKey
- get() =
- if (shadeMode.value is ShadeMode.Dual) {
- Scenes.NotificationsShade
- } else {
- Scenes.Shade
- }
-
- private val quickSettingsScene: SceneKey
- get() =
- if (shadeMode.value is ShadeMode.Dual) {
- Scenes.QuickSettingsShade
- } else {
- Scenes.QuickSettings
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
index e7fc18e..558f179 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
@@ -17,12 +17,11 @@
package com.android.systemui.shade.domain.interactor
import com.android.keyguard.LockIconViewController
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.data.repository.ShadeRepository
-import com.android.systemui.shade.shared.model.ShadeMode
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
@@ -31,7 +30,6 @@
class ShadeLockscreenInteractorImpl
@Inject
constructor(
- @Application private val applicationScope: CoroutineScope,
@Background private val backgroundScope: CoroutineScope,
private val shadeInteractor: ShadeInteractor,
private val sceneInteractor: SceneInteractor,
@@ -69,6 +67,7 @@
override fun setPulsing(pulsing: Boolean) {
// Now handled elsewhere. Do nothing.
}
+
override fun transitionToExpandedShade(delay: Long) {
backgroundScope.launch {
delay(delay)
@@ -98,12 +97,9 @@
}
private fun changeToShadeScene() {
- applicationScope.launch {
- val shadeMode = shadeInteractor.shadeMode.value
- sceneInteractor.changeScene(
- if (shadeMode is ShadeMode.Dual) Scenes.NotificationsShade else Scenes.Shade,
- "ShadeLockscreenInteractorImpl.expandToNotifications",
- )
- }
+ sceneInteractor.changeScene(
+ SceneFamilies.NotifShade,
+ "ShadeLockscreenInteractorImpl.expandToNotifications",
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
index 6c76061..b2e0cd0 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
@@ -30,6 +30,9 @@
import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.privacy.PrivacyItem
import com.android.systemui.res.R
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.model.SceneFamilies
+import com.android.systemui.scene.shared.model.TransitionKeys
import com.android.systemui.shade.domain.interactor.PrivacyChipInteractor
import com.android.systemui.shade.domain.interactor.ShadeHeaderClockInteractor
import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -57,6 +60,7 @@
@Application private val applicationScope: CoroutineScope,
context: Context,
private val activityStarter: ActivityStarter,
+ private val sceneInteractor: SceneInteractor,
shadeInteractor: ShadeInteractor,
mobileIconsInteractor: MobileIconsInteractor,
val mobileIconsViewModel: MobileIconsViewModel,
@@ -139,6 +143,15 @@
clockInteractor.launchClockActivity()
}
+ /** Notifies that the system icons container was clicked. */
+ fun onSystemIconContainerClicked() {
+ sceneInteractor.changeScene(
+ SceneFamilies.Home,
+ "ShadeHeaderViewModel.onSystemIconContainerClicked",
+ TransitionKeys.SlightlyFasterShadeCollapse,
+ )
+ }
+
/** Notifies that the shadeCarrierGroup was clicked. */
fun onShadeCarrierGroupClicked() {
activityStarter.postStartActivityDismissingKeyguard(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java b/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java
index 2dfc920..abf258c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java
@@ -24,6 +24,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.ViewRootImpl.CLIENT_IMMERSIVE_CONFIRMATION;
import static android.view.ViewRootImpl.CLIENT_TRANSIENT;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID;
@@ -36,12 +37,11 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.res.ColorStateList;
import android.content.res.Resources;
-import android.content.res.TypedArray;
import android.database.ContentObserver;
import android.graphics.Insets;
import android.graphics.PixelFormat;
+import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.os.Binder;
import android.os.Bundle;
@@ -71,7 +71,7 @@
import android.view.animation.Interpolator;
import android.widget.Button;
import android.widget.FrameLayout;
-import android.widget.ImageView;
+import android.widget.RelativeLayout;
import com.android.systemui.CoreStartable;
import com.android.systemui.res.R;
@@ -263,6 +263,7 @@
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
PixelFormat.TRANSLUCENT);
lp.setFitInsetsTypes(lp.getFitInsetsTypes() & ~Type.statusBars());
+ lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
// Trusted overlay so touches outside the touchable area are allowed to pass through
lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
| WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
@@ -275,13 +276,20 @@
private FrameLayout.LayoutParams getBubbleLayoutParams() {
return new FrameLayout.LayoutParams(
- mSysUiContext.getResources().getDimensionPixelSize(
- R.dimen.immersive_mode_cling_width),
+ getClingWindowWidth(),
ViewGroup.LayoutParams.WRAP_CONTENT,
Gravity.CENTER_HORIZONTAL | Gravity.TOP);
}
/**
+ * Returns the width of the cling window.
+ */
+ private int getClingWindowWidth() {
+ return mSysUiContext.getResources().getDimensionPixelSize(
+ R.dimen.immersive_mode_cling_width);
+ }
+
+ /**
* @return the window token that's used by all ImmersiveModeConfirmation windows.
*/
IBinder getWindowToken() {
@@ -409,21 +417,6 @@
mClingLayout = (ViewGroup)
View.inflate(mSysUiContext, R.layout.immersive_mode_cling, null);
- TypedArray ta = mDisplayContext.obtainStyledAttributes(
- new int[]{android.R.attr.colorAccent});
- int colorAccent = ta.getColor(0, 0);
- ta.recycle();
- mClingLayout.setBackgroundColor(colorAccent);
- ImageView expandMore = mClingLayout.findViewById(R.id.immersive_cling_ic_expand_more);
- if (expandMore != null) {
- expandMore.setImageTintList(ColorStateList.valueOf(colorAccent));
- }
- ImageView lightBgCirc = mClingLayout.findViewById(R.id.immersive_cling_back_bg_light);
- if (lightBgCirc != null) {
- // Set transparency to 50%
- lightBgCirc.setImageAlpha(128);
- }
-
final Button ok = mClingLayout.findViewById(R.id.ok);
ok.setOnClickListener(new OnClickListener() {
@Override
@@ -482,6 +475,24 @@
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ // If the top display cutout overlaps with the full-width (windowWidth=-1)/centered
+ // dialog, then adjust the dialog contents by the cutout
+ final int width = getWidth();
+ final int windowWidth = getClingWindowWidth();
+ final Rect topDisplayCutout = insets.getDisplayCutout() != null
+ ? insets.getDisplayCutout().getBoundingRectTop()
+ : new Rect();
+ final boolean intersectsTopCutout = topDisplayCutout.intersects(
+ width - (windowWidth / 2), 0,
+ width + (windowWidth / 2), topDisplayCutout.bottom);
+ if (mClingWindow != null &&
+ (windowWidth < 0 || (width > 0 && intersectsTopCutout))) {
+ final View iconView = mClingWindow.findViewById(R.id.immersive_cling_icon);
+ RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams)
+ iconView.getLayoutParams();
+ lp.topMargin = topDisplayCutout.bottom;
+ iconView.setLayoutParams(lp);
+ }
// we will be hiding the nav bar, so layout as if it's already hidden
return new WindowInsets.Builder(insets).setInsets(
Type.systemBars(), Insets.NONE).build();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractor.kt
index c3d37fb..086a32d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractor.kt
@@ -16,11 +16,37 @@
package com.android.systemui.statusbar.chips.domain.interactor
+import android.view.View
+import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.res.R
import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
+import com.android.systemui.statusbar.phone.SystemUIDialog
import kotlinx.coroutines.flow.StateFlow
/** Interface for an interactor that knows the state of a single type of ongoing activity chip. */
interface OngoingActivityChipInteractor {
/** A flow modeling the chip that should be shown. */
val chip: StateFlow<OngoingActivityChipModel>
+
+ companion object {
+ /** Creates a chip click listener that launches a dialog created by [dialogDelegate]. */
+ fun createDialogLaunchOnClickListener(
+ dialogDelegate: SystemUIDialog.Delegate,
+ dialogTransitionAnimator: DialogTransitionAnimator,
+ ): View.OnClickListener {
+ return View.OnClickListener { view ->
+ val dialog = dialogDelegate.createDialog()
+ val launchableView =
+ view.requireViewById<ChipBackgroundContainer>(
+ R.id.ongoing_activity_chip_background
+ )
+ // TODO(b/343699052): This makes a beautiful animate-in, but the
+ // animate-out looks odd because the dialog animates back into the chip
+ // but then the chip disappears. If we aren't able to address
+ // b/343699052 in time for launch, we should just use `dialog.show`.
+ dialogTransitionAnimator.showFromView(dialog, launchableView)
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt
index ac16d26..f6fbe38 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt
@@ -16,6 +16,9 @@
package com.android.systemui.statusbar.chips.mediaprojection.domain.interactor
+import android.content.pm.PackageManager
+import androidx.annotation.DrawableRes
+import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
@@ -24,7 +27,12 @@
import com.android.systemui.mediaprojection.data.repository.MediaProjectionRepository
import com.android.systemui.res.R
import com.android.systemui.statusbar.chips.domain.interactor.OngoingActivityChipInteractor
+import com.android.systemui.statusbar.chips.domain.interactor.OngoingActivityChipInteractor.Companion.createDialogLaunchOnClickListener
import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndCastToOtherDeviceDialogDelegate
+import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndMediaProjectionDialogHelper
+import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndShareToAppDialogDelegate
+import com.android.systemui.util.Utils
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -32,6 +40,7 @@
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
/**
* Interactor for media-projection-related chips in the status bar.
@@ -47,33 +56,97 @@
class MediaProjectionChipInteractor
@Inject
constructor(
- @Application scope: CoroutineScope,
- mediaProjectionRepository: MediaProjectionRepository,
- val systemClock: SystemClock,
+ @Application private val scope: CoroutineScope,
+ private val mediaProjectionRepository: MediaProjectionRepository,
+ private val packageManager: PackageManager,
+ private val systemClock: SystemClock,
+ private val dialogTransitionAnimator: DialogTransitionAnimator,
+ private val endMediaProjectionDialogHelper: EndMediaProjectionDialogHelper,
) : OngoingActivityChipInteractor {
override val chip: StateFlow<OngoingActivityChipModel> =
mediaProjectionRepository.mediaProjectionState
.map { state ->
when (state) {
is MediaProjectionState.NotProjecting -> OngoingActivityChipModel.Hidden
- is MediaProjectionState.EntireScreen,
- is MediaProjectionState.SingleTask -> {
- // TODO(b/332662551): Distinguish between cast-to-other-device and
- // share-to-app.
- OngoingActivityChipModel.Shown(
- icon =
- Icon.Resource(
- R.drawable.ic_cast_connected,
- ContentDescription.Resource(R.string.accessibility_casting)
- ),
- // TODO(b/332662551): See if we can use a MediaProjection API to fetch
- // this time.
- startTimeMs = systemClock.elapsedRealtime()
- ) {
- // TODO(b/332662551): Implement the pause dialog.
+ is MediaProjectionState.Projecting -> {
+ if (isProjectionToOtherDevice(state.hostPackage)) {
+ createCastToOtherDeviceChip(state)
+ } else {
+ createShareToAppChip(state)
}
}
}
}
.stateIn(scope, SharingStarted.WhileSubscribed(), OngoingActivityChipModel.Hidden)
+
+ /** Stops the currently active projection. */
+ fun stopProjecting() {
+ scope.launch { mediaProjectionRepository.stopProjecting() }
+ }
+
+ /**
+ * Returns true iff projecting to the given [packageName] means that we're projecting to a
+ * *different* device (as opposed to projecting to some application on *this* device).
+ */
+ private fun isProjectionToOtherDevice(packageName: String?): Boolean {
+ // The [isHeadlessRemoteDisplayProvider] check approximates whether a projection is to a
+ // different device or the same device, because headless remote display packages are the
+ // only kinds of packages that do cast-to-other-device. This isn't exactly perfect,
+ // because it means that any projection by those headless remote display packages will be
+ // marked as going to a different device, even if that isn't always true. See b/321078669.
+ return Utils.isHeadlessRemoteDisplayProvider(packageManager, packageName)
+ }
+
+ private fun createCastToOtherDeviceChip(
+ state: MediaProjectionState.Projecting,
+ ): OngoingActivityChipModel.Shown {
+ return OngoingActivityChipModel.Shown(
+ icon =
+ Icon.Resource(
+ CAST_TO_OTHER_DEVICE_ICON,
+ ContentDescription.Resource(R.string.accessibility_casting)
+ ),
+ // TODO(b/332662551): Maybe use a MediaProjection API to fetch this time.
+ startTimeMs = systemClock.elapsedRealtime(),
+ createDialogLaunchOnClickListener(
+ createCastToOtherDeviceDialogDelegate(state),
+ dialogTransitionAnimator,
+ ),
+ )
+ }
+
+ private fun createCastToOtherDeviceDialogDelegate(state: MediaProjectionState.Projecting) =
+ EndCastToOtherDeviceDialogDelegate(
+ endMediaProjectionDialogHelper,
+ this@MediaProjectionChipInteractor,
+ state,
+ )
+
+ private fun createShareToAppChip(
+ state: MediaProjectionState.Projecting,
+ ): OngoingActivityChipModel.Shown {
+ return OngoingActivityChipModel.Shown(
+ // TODO(b/332662551): Use the right content description.
+ icon = Icon.Resource(SHARE_TO_APP_ICON, contentDescription = null),
+ // TODO(b/332662551): Maybe use a MediaProjection API to fetch this time.
+ startTimeMs = systemClock.elapsedRealtime(),
+ createDialogLaunchOnClickListener(
+ createShareToAppDialogDelegate(state),
+ dialogTransitionAnimator
+ ),
+ )
+ }
+
+ private fun createShareToAppDialogDelegate(state: MediaProjectionState.Projecting) =
+ EndShareToAppDialogDelegate(
+ endMediaProjectionDialogHelper,
+ this@MediaProjectionChipInteractor,
+ state,
+ )
+
+ companion object {
+ // TODO(b/332662551): Use the right icon.
+ @DrawableRes val SHARE_TO_APP_ICON = R.drawable.ic_screenshot_share
+ @DrawableRes val CAST_TO_OTHER_DEVICE_ICON = R.drawable.ic_cast_connected
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegate.kt
new file mode 100644
index 0000000..596fbf8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegate.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips.mediaprojection.ui.view
+
+import android.os.Bundle
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractor
+import com.android.systemui.statusbar.phone.SystemUIDialog
+
+/** A dialog that lets the user stop an ongoing cast-screen-to-other-device event. */
+class EndCastToOtherDeviceDialogDelegate(
+ private val endMediaProjectionDialogHelper: EndMediaProjectionDialogHelper,
+ private val interactor: MediaProjectionChipInteractor,
+ private val state: MediaProjectionState.Projecting,
+) : SystemUIDialog.Delegate {
+ override fun createDialog(): SystemUIDialog {
+ return endMediaProjectionDialogHelper.createDialog(this)
+ }
+
+ override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
+ with(dialog) {
+ setIcon(MediaProjectionChipInteractor.CAST_TO_OTHER_DEVICE_ICON)
+ setTitle(R.string.cast_to_other_device_stop_dialog_title)
+ setMessage(
+ endMediaProjectionDialogHelper.getDialogMessage(
+ state,
+ genericMessageResId = R.string.cast_to_other_device_stop_dialog_message,
+ specificAppMessageResId =
+ R.string.cast_to_other_device_stop_dialog_message_specific_app,
+ )
+ )
+ // No custom on-click, because the dialog will automatically be dismissed when the
+ // button is clicked anyway.
+ setNegativeButton(R.string.close_dialog_button, /* onClick= */ null)
+ setPositiveButton(R.string.cast_to_other_device_stop_dialog_button) { _, _ ->
+ interactor.stopProjecting()
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelper.kt
new file mode 100644
index 0000000..347be02
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelper.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips.mediaprojection.ui.view
+
+import android.annotation.StringRes
+import android.content.Context
+import android.content.pm.PackageManager
+import android.text.Html
+import android.text.Html.FROM_HTML_MODE_LEGACY
+import android.text.TextUtils
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import javax.inject.Inject
+
+/** Helper class for showing dialogs that let users end different types of media projections. */
+@SysUISingleton
+class EndMediaProjectionDialogHelper
+@Inject
+constructor(
+ private val dialogFactory: SystemUIDialog.Factory,
+ private val packageManager: PackageManager,
+ private val context: Context
+) {
+ /** Creates a new [SystemUIDialog] using the given delegate. */
+ fun createDialog(delegate: SystemUIDialog.Delegate): SystemUIDialog {
+ return dialogFactory.create(delegate)
+ }
+
+ /**
+ * Returns the message to show in the dialog based on the specific media projection state.
+ *
+ * @param genericMessageResId a res ID for a more generic "end projection" message
+ * @param specificAppMessageResId a res ID for an "end projection" message that also lets us
+ * specify which app is currently being projected.
+ */
+ fun getDialogMessage(
+ state: MediaProjectionState.Projecting,
+ @StringRes genericMessageResId: Int,
+ @StringRes specificAppMessageResId: Int,
+ ): CharSequence {
+ when (state) {
+ is MediaProjectionState.Projecting.EntireScreen ->
+ return context.getString(genericMessageResId)
+ is MediaProjectionState.Projecting.SingleTask -> {
+ val packageName =
+ state.task.baseIntent.component?.packageName
+ ?: return context.getString(genericMessageResId)
+ try {
+ val appInfo = packageManager.getApplicationInfo(packageName, 0)
+ val appName = appInfo.loadLabel(packageManager)
+ return getSpecificAppMessageText(specificAppMessageResId, appName)
+ } catch (e: PackageManager.NameNotFoundException) {
+ // TODO(b/332662551): Log this error.
+ return context.getString(genericMessageResId)
+ }
+ }
+ }
+ }
+
+ private fun getSpecificAppMessageText(
+ @StringRes specificAppMessageResId: Int,
+ appName: CharSequence,
+ ): CharSequence {
+ // https://developer.android.com/guide/topics/resources/string-resource#StylingWithHTML
+ val escapedAppName = TextUtils.htmlEncode(appName.toString())
+ val text = context.getString(specificAppMessageResId, escapedAppName)
+ return Html.fromHtml(text, FROM_HTML_MODE_LEGACY)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegate.kt
new file mode 100644
index 0000000..749a11f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegate.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips.mediaprojection.ui.view
+
+import android.os.Bundle
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractor
+import com.android.systemui.statusbar.phone.SystemUIDialog
+
+/** A dialog that lets the user stop an ongoing share-screen-to-app event. */
+class EndShareToAppDialogDelegate(
+ private val endMediaProjectionDialogHelper: EndMediaProjectionDialogHelper,
+ private val interactor: MediaProjectionChipInteractor,
+ private val state: MediaProjectionState.Projecting,
+) : SystemUIDialog.Delegate {
+ override fun createDialog(): SystemUIDialog {
+ return endMediaProjectionDialogHelper.createDialog(this)
+ }
+
+ override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
+ with(dialog) {
+ setIcon(MediaProjectionChipInteractor.SHARE_TO_APP_ICON)
+ setTitle(R.string.share_to_app_stop_dialog_title)
+ setMessage(
+ endMediaProjectionDialogHelper.getDialogMessage(
+ state,
+ genericMessageResId = R.string.share_to_app_stop_dialog_message,
+ specificAppMessageResId = R.string.share_to_app_stop_dialog_message_specific_app
+ )
+ )
+ // No custom on-click, because the dialog will automatically be dismissed when the
+ // button is clicked anyway.
+ setNegativeButton(R.string.close_dialog_button, /* onClick= */ null)
+ setPositiveButton(R.string.share_to_app_stop_dialog_button) { _, _ ->
+ interactor.stopProjecting()
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
index 585ff5f..4959b09 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.chips.screenrecord.domain.interactor
+import androidx.annotation.DrawableRes
+import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -23,7 +25,10 @@
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
import com.android.systemui.screenrecord.data.repository.ScreenRecordRepository
import com.android.systemui.statusbar.chips.domain.interactor.OngoingActivityChipInteractor
+import com.android.systemui.statusbar.chips.domain.interactor.OngoingActivityChipInteractor.Companion.createDialogLaunchOnClickListener
import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.screenrecord.ui.view.EndScreenRecordingDialogDelegate
+import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -31,15 +36,18 @@
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
/** Interactor for the screen recording chip shown in the status bar. */
@SysUISingleton
class ScreenRecordChipInteractor
@Inject
constructor(
- @Application scope: CoroutineScope,
- screenRecordRepository: ScreenRecordRepository,
- val systemClock: SystemClock,
+ @Application private val scope: CoroutineScope,
+ private val screenRecordRepository: ScreenRecordRepository,
+ private val systemClock: SystemClock,
+ private val dialogFactory: SystemUIDialog.Factory,
+ private val dialogTransitionAnimator: DialogTransitionAnimator,
) : OngoingActivityChipInteractor {
override val chip: StateFlow<OngoingActivityChipModel> =
screenRecordRepository.screenRecordState
@@ -51,16 +59,29 @@
is ScreenRecordModel.Recording ->
OngoingActivityChipModel.Shown(
// TODO(b/332662551): Also provide a content description.
- icon =
- Icon.Resource(
- R.drawable.stat_sys_screen_record,
- contentDescription = null
- ),
- startTimeMs = systemClock.elapsedRealtime()
- ) {
- // TODO(b/332662551): Implement the pause dialog.
- }
+ icon = Icon.Resource(ICON, contentDescription = null),
+ startTimeMs = systemClock.elapsedRealtime(),
+ createDialogLaunchOnClickListener(
+ dialogDelegate,
+ dialogTransitionAnimator
+ ),
+ )
}
}
.stateIn(scope, SharingStarted.WhileSubscribed(), OngoingActivityChipModel.Hidden)
+
+ /** Stops the recording. */
+ fun stopRecording() {
+ scope.launch { screenRecordRepository.stopRecording() }
+ }
+
+ private val dialogDelegate =
+ EndScreenRecordingDialogDelegate(
+ dialogFactory,
+ this@ScreenRecordChipInteractor,
+ )
+
+ companion object {
+ @DrawableRes val ICON = R.drawable.ic_screenrecord
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegate.kt
new file mode 100644
index 0000000..b8e8cfa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegate.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips.screenrecord.ui.view
+
+import android.os.Bundle
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.ScreenRecordChipInteractor
+import com.android.systemui.statusbar.phone.SystemUIDialog
+
+/** A dialog that lets the user stop an ongoing screen recording. */
+class EndScreenRecordingDialogDelegate(
+ private val systemUIDialogFactory: SystemUIDialog.Factory,
+ private val interactor: ScreenRecordChipInteractor,
+) : SystemUIDialog.Delegate {
+
+ override fun createDialog(): SystemUIDialog {
+ return systemUIDialogFactory.create(this)
+ }
+
+ override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
+ with(dialog) {
+ setIcon(ScreenRecordChipInteractor.ICON)
+ setTitle(R.string.screenrecord_stop_dialog_title)
+ // TODO(b/332662551): Use a different message if they're sharing just a single app.
+ setMessage(R.string.screenrecord_stop_dialog_message)
+ // No custom on-click, because the dialog will automatically be dismissed when the
+ // button is clicked anyway.
+ setNegativeButton(R.string.close_dialog_button, /* onClick= */ null)
+ setPositiveButton(R.string.screenrecord_stop_dialog_button) { _, _ ->
+ interactor.stopRecording()
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
index 11636bd..f95e0fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
@@ -37,6 +37,7 @@
import com.android.systemui.statusbar.phone.LetterboxAppearanceCalculator
import com.android.systemui.statusbar.phone.StatusBarBoundsProvider
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
+import com.android.systemui.statusbar.phone.ongoingcall.data.model.OngoingCallModel
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@@ -224,8 +225,8 @@
modifiedStatusBarAttributes,
isTransientShown,
isInFullscreenMode,
- ongoingCallRepository.hasOngoingCall,
- ) { modifiedAttributes, isTransientShown, isInFullscreenMode, hasOngoingCall ->
+ ongoingCallRepository.ongoingCallState,
+ ) { modifiedAttributes, isTransientShown, isInFullscreenMode, ongoingCallState ->
if (modifiedAttributes == null) {
null
} else {
@@ -234,7 +235,7 @@
modifiedAttributes.appearance,
isTransientShown,
isInFullscreenMode,
- hasOngoingCall,
+ hasOngoingCall = ongoingCallState is OngoingCallModel.InCall,
)
StatusBarAppearance(
statusBarMode,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index 2e87a5b..ee2c9cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -38,6 +38,7 @@
import android.view.ViewGroup
import androidx.annotation.VisibleForTesting
import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.settingslib.Utils
import com.android.systemui.Dumpable
import com.android.systemui.Flags.smartspaceLockscreenViewmodel
@@ -53,6 +54,7 @@
import com.android.systemui.plugins.BcSmartspaceDataPlugin
import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener
import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView
+import com.android.systemui.plugins.BcSmartspaceDataPlugin.TimeChangedDelegate
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.clocks.WeatherData
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -411,6 +413,7 @@
val ssView = plugin.getView(parent)
configPlugin?.let { ssView.registerConfigProvider(it) }
ssView.setUiSurface(BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
+ ssView.setTimeChangedDelegate(SmartspaceTimeChangedDelegate(keyguardUpdateMonitor))
ssView.registerDataProvider(plugin)
ssView.setIntentStarter(object : BcSmartspaceDataPlugin.IntentStarter {
@@ -682,5 +685,28 @@
}
}
}
+
+ private class SmartspaceTimeChangedDelegate(
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor
+ ) : TimeChangedDelegate {
+ private var keyguardUpdateMonitorCallback: KeyguardUpdateMonitorCallback? = null
+ override fun register(callback: Runnable) {
+ if (keyguardUpdateMonitorCallback != null) {
+ unregister()
+ }
+ keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() {
+ override fun onTimeChanged() {
+ callback.run()
+ }
+ }
+ keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
+ callback.run()
+ }
+
+ override fun unregister() {
+ keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback)
+ keyguardUpdateMonitorCallback = null
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/AvalancheProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/AvalancheProvider.kt
index c29d700..a8fd082 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/AvalancheProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/AvalancheProvider.kt
@@ -24,6 +24,7 @@
import com.android.internal.logging.UiEventLogger
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.interruption.AvalancheSuppressor.AvalancheEvent
import javax.inject.Inject
// Class to track avalanche trigger event time.
@@ -31,37 +32,41 @@
class AvalancheProvider
@Inject
constructor(
- private val broadcastDispatcher: BroadcastDispatcher,
- private val logger: VisualInterruptionDecisionLogger,
- private val uiEventLogger: UiEventLogger,
+ private val broadcastDispatcher: BroadcastDispatcher,
+ private val logger: VisualInterruptionDecisionLogger,
+ private val uiEventLogger: UiEventLogger,
) {
val TAG = "AvalancheProvider"
val timeoutMs = 120000
var startTime: Long = 0L
- private val avalancheTriggerIntents = mutableSetOf(
+ private val avalancheTriggerIntents =
+ mutableSetOf(
Intent.ACTION_AIRPLANE_MODE_CHANGED,
Intent.ACTION_BOOT_COMPLETED,
Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
Intent.ACTION_USER_SWITCHED
- )
+ )
- private val broadcastReceiver: BroadcastReceiver = object : BroadcastReceiver() {
- override fun onReceive(context: Context, intent: Intent) {
- if (intent.action in avalancheTriggerIntents) {
+ private val broadcastReceiver: BroadcastReceiver =
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ if (intent.action in avalancheTriggerIntents) {
- // Ignore when airplane mode turned on
- if (intent.action == Intent.ACTION_AIRPLANE_MODE_CHANGED
- && intent.getBooleanExtra(/* name= */ "state", /* defaultValue */ false)) {
- Log.d(TAG, "broadcastReceiver: ignore airplane mode on")
- return
+ // Ignore when airplane mode turned on
+ if (
+ intent.action == Intent.ACTION_AIRPLANE_MODE_CHANGED &&
+ intent.getBooleanExtra(/* name= */ "state", /* defaultValue */ false)
+ ) {
+ Log.d(TAG, "broadcastReceiver: ignore airplane mode on")
+ return
+ }
+ Log.d(TAG, "broadcastReceiver received intent.action=" + intent.action)
+ uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_RECEIVED_TRIGGERING_EVENT)
+ startTime = System.currentTimeMillis()
}
- Log.d(TAG, "broadcastReceiver received intent.action=" + intent.action)
- uiEventLogger.log(AvalancheSuppressor.AvalancheEvent.START);
- startTime = System.currentTimeMillis()
}
}
- }
fun register() {
val intentFilter = IntentFilter()
@@ -70,4 +75,4 @@
}
broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter)
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
index f84b5f4..367aaad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
@@ -270,32 +270,26 @@
}
enum class AvalancheEvent(private val id: Int) : UiEventLogger.UiEventEnum {
- @UiEvent(
- doc =
- "An avalanche event occurred but this notification was suppressed by a " +
- "non-avalanche suppressor."
- )
- START(1802),
- @UiEvent(doc = "HUN was suppressed in avalanche.") SUPPRESS(1803),
- @UiEvent(doc = "HUN allowed during avalanche because it is high priority.")
- ALLOW_CONVERSATION_AFTER_AVALANCHE(1804),
- @UiEvent(doc = "HUN allowed during avalanche because it is a high priority conversation.")
- ALLOW_HIGH_PRIORITY_CONVERSATION_ANY_TIME(1805),
- @UiEvent(doc = "HUN allowed during avalanche because it is a call.") ALLOW_CALLSTYLE(1806),
+ @UiEvent(doc = "An avalanche event occurred, and a suppression period will start now.")
+ AVALANCHE_SUPPRESSOR_RECEIVED_TRIGGERING_EVENT(1824),
+ @UiEvent(doc = "HUN was suppressed in avalanche.")
+ AVALANCHE_SUPPRESSOR_HUN_SUPPRESSED(1825),
+ @UiEvent(doc = "HUN allowed during avalanche because conversation newer than the trigger.")
+ AVALANCHE_SUPPRESSOR_HUN_ALLOWED_NEW_CONVERSATION(1826),
+ @UiEvent(doc = "HUN allowed during avalanche because it is a priority conversation.")
+ AVALANCHE_SUPPRESSOR_HUN_ALLOWED_PRIORITY_CONVERSATION(1827),
+ @UiEvent(doc = "HUN allowed during avalanche because it is a CallStyle notification.")
+ AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CALL_STYLE(1828),
+ @UiEvent(doc = "HUN allowed during avalanche because it is a reminder notification.")
+ AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CATEGORY_REMINDER(1829),
@UiEvent(doc = "HUN allowed during avalanche because it is a calendar notification.")
- ALLOW_CATEGORY_REMINDER(1807),
- @UiEvent(doc = "HUN allowed during avalanche because it is a calendar notification.")
- ALLOW_CATEGORY_EVENT(1808),
- @UiEvent(
- doc =
- "HUN allowed during avalanche because it has a full screen intent and " +
- "the full screen intent permission is granted."
- )
- ALLOW_FSI_WITH_PERMISSION_ON(1809),
+ AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CATEGORY_EVENT(1830),
+ @UiEvent(doc = "HUN allowed during avalanche because it has FSI.")
+ AVALANCHE_SUPPRESSOR_HUN_ALLOWED_FSI_WITH_PERMISSION(1831),
@UiEvent(doc = "HUN allowed during avalanche because it is colorized.")
- ALLOW_COLORIZED(1810),
+ AVALANCHE_SUPPRESSOR_HUN_ALLOWED_COLORIZED(1832),
@UiEvent(doc = "HUN allowed during avalanche because it is an emergency notification.")
- ALLOW_EMERGENCY(1811);
+ AVALANCHE_SUPPRESSOR_HUN_ALLOWED_EMERGENCY(1833);
override fun getId(): Int {
return id
@@ -323,46 +317,46 @@
entry.ranking.isConversation &&
entry.sbn.notification.getWhen() > avalancheProvider.startTime
) {
- uiEventLogger.log(AvalancheEvent.ALLOW_CONVERSATION_AFTER_AVALANCHE)
+ uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_NEW_CONVERSATION)
return State.ALLOW_CONVERSATION_AFTER_AVALANCHE
}
if (entry.channel?.isImportantConversation == true) {
- uiEventLogger.log(AvalancheEvent.ALLOW_HIGH_PRIORITY_CONVERSATION_ANY_TIME)
+ uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_PRIORITY_CONVERSATION)
return State.ALLOW_HIGH_PRIORITY_CONVERSATION_ANY_TIME
}
if (entry.sbn.notification.isStyle(Notification.CallStyle::class.java)) {
- uiEventLogger.log(AvalancheEvent.ALLOW_CALLSTYLE)
+ uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CALL_STYLE)
return State.ALLOW_CALLSTYLE
}
if (entry.sbn.notification.category == CATEGORY_REMINDER) {
- uiEventLogger.log(AvalancheEvent.ALLOW_CATEGORY_REMINDER)
+ uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CATEGORY_REMINDER)
return State.ALLOW_CATEGORY_REMINDER
}
if (entry.sbn.notification.category == CATEGORY_EVENT) {
- uiEventLogger.log(AvalancheEvent.ALLOW_CATEGORY_EVENT)
+ uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CATEGORY_EVENT)
return State.ALLOW_CATEGORY_EVENT
}
if (entry.sbn.notification.fullScreenIntent != null) {
- uiEventLogger.log(AvalancheEvent.ALLOW_FSI_WITH_PERMISSION_ON)
+ uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_FSI_WITH_PERMISSION)
return State.ALLOW_FSI_WITH_PERMISSION_ON
}
if (entry.sbn.notification.isColorized) {
- uiEventLogger.log(AvalancheEvent.ALLOW_COLORIZED)
+ uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_COLORIZED)
return State.ALLOW_COLORIZED
}
if (
packageManager.checkPermission(RECEIVE_EMERGENCY_BROADCAST, entry.sbn.packageName) ==
PERMISSION_GRANTED
) {
- uiEventLogger.log(AvalancheEvent.ALLOW_EMERGENCY)
+ uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_EMERGENCY)
return State.ALLOW_EMERGENCY
}
- uiEventLogger.log(AvalancheEvent.SUPPRESS)
+ uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_SUPPRESSED)
return State.SUPPRESS
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index d1fabb1..9394249 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -358,11 +358,7 @@
* @param whenMillis
*/
public void setNotificationWhen(long whenMillis) {
- if (mNotificationHeader == null) {
- return;
- }
-
- final View timeView = mNotificationHeader.findViewById(com.android.internal.R.id.time);
+ final View timeView = mView.findViewById(com.android.internal.R.id.time);
if (timeView instanceof DateTimeView) {
((DateTimeView) timeView).setTime(whenMillis);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index fe22cc6..71a0b94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -833,6 +833,23 @@
int y = 0;
drawDebugInfo(canvas, y, Color.RED, /* label= */ "y = " + y);
+ if (SceneContainerFlag.isEnabled()) {
+ y = (int) mScrollViewFields.getStackTop();
+ drawDebugInfo(canvas, y, Color.RED, /* label= */ "getStackTop() = " + y);
+
+ y = (int) mScrollViewFields.getStackBottom();
+ drawDebugInfo(canvas, y, Color.MAGENTA, /* label= */ "getStackBottom() = " + y);
+
+ y = (int) mScrollViewFields.getHeadsUpTop();
+ drawDebugInfo(canvas, y, Color.GREEN, /* label= */ "getHeadsUpTop() = " + y);
+
+ y += getTopHeadsUpHeight();
+ drawDebugInfo(canvas, y, Color.BLUE,
+ /* label= */ "getHeadsUpTop() + getTopHeadsUpHeight() = " + y);
+
+ return; // the rest of the fields are not important in Flexiglass
+ }
+
y = getTopPadding();
drawDebugInfo(canvas, y, Color.RED, /* label= */ "getTopPadding() = " + y);
@@ -3471,6 +3488,7 @@
}
if (isUpOrCancel) {
+ mScrollViewFields.sendCurrentGestureOverscroll(false);
setIsBeingDragged(false);
}
return false;
@@ -3606,7 +3624,6 @@
if (mIsBeingDragged) {
// Defer actual scrolling to the scene framework if enabled
if (SceneContainerFlag.isEnabled()) {
- setIsBeingDragged(false);
return false;
}
// Scroll to follow the motion event
@@ -3704,7 +3721,7 @@
protected boolean isInsideQsHeader(MotionEvent ev) {
if (SceneContainerFlag.isEnabled()) {
- return ev.getY() < mScrollViewFields.getScrimClippingShape().getBounds().getTop();
+ return ev.getY() < mScrollViewFields.getStackTop();
}
mQsHeader.getBoundsOnScreen(mQsHeaderBound);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
index e90a64a..85835d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
@@ -24,6 +24,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.shared.model.ShadeMode
@@ -36,7 +37,6 @@
import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOf
@@ -50,7 +50,7 @@
dumpManager: DumpManager,
stackAppearanceInteractor: NotificationStackAppearanceInteractor,
shadeInteractor: ShadeInteractor,
- sceneInteractor: SceneInteractor,
+ private val sceneInteractor: SceneInteractor,
// TODO(b/336364825) Remove Lazy when SceneContainerFlag is released -
// while the flag is off, creating this object too early results in a crash
keyguardInteractor: Lazy<KeyguardInteractor>,
@@ -63,9 +63,11 @@
val expandFraction: Flow<Float> =
combine(
shadeInteractor.shadeExpansion,
+ shadeInteractor.shadeMode,
shadeInteractor.qsExpansion,
sceneInteractor.transitionState,
- ) { shadeExpansion, qsExpansion, transitionState ->
+ sceneInteractor.resolveSceneFamily(SceneFamilies.QuickSettings),
+ ) { shadeExpansion, shadeMode, qsExpansion, transitionState, quickSettingsScene ->
when (transitionState) {
is ObservableTransitionState.Idle -> {
if (transitionState.currentScene == Scenes.Lockscreen) {
@@ -76,16 +78,16 @@
}
is ObservableTransitionState.Transition -> {
if (
- (transitionState.fromScene == notificationsScene &&
+ (transitionState.fromScene in SceneFamilies.NotifShade &&
transitionState.toScene == quickSettingsScene) ||
- (transitionState.fromScene == quickSettingsScene &&
- transitionState.toScene == notificationsScene)
+ (transitionState.fromScene in quickSettingsScene &&
+ transitionState.toScene in SceneFamilies.NotifShade)
) {
1f
} else if (
- (transitionState.fromScene == Scenes.Gone ||
- transitionState.fromScene == Scenes.Lockscreen) &&
- transitionState.toScene == quickSettingsScene
+ shadeMode != ShadeMode.Split &&
+ transitionState.fromScene in SceneFamilies.Home &&
+ transitionState.toScene in quickSettingsScene
) {
// during QS expansion, increase fraction at same rate as scrim alpha,
// but start when scrim alpha is at EXPANSION_FOR_DELAYED_STACK_FADE_IN.
@@ -101,6 +103,9 @@
.distinctUntilChanged()
.dumpWhileCollecting("expandFraction")
+ private operator fun SceneKey.contains(scene: SceneKey) =
+ sceneInteractor.isSceneInFamily(scene, this)
+
/** The bounds of the notification stack in the current scene. */
private val shadeScrimClipping: Flow<ShadeScrimClipping?> =
combine(
@@ -142,6 +147,7 @@
/** Receives the amount (px) that the stack should scroll due to internal expansion. */
val syntheticScrollConsumer: (Float) -> Unit = stackAppearanceInteractor::setSyntheticScroll
+
/**
* Receives whether the current touch gesture is overscroll as it has already been consumed by
* the stack.
@@ -150,10 +156,9 @@
stackAppearanceInteractor::setCurrentGestureOverscroll
/** Whether the notification stack is scrollable or not. */
- val isScrollable: Flow<Boolean> =
- sceneInteractor.currentScene
- .map { it == notificationsScene }
- .dumpWhileCollecting("isScrollable")
+ val isScrollable: Flow<Boolean> = sceneInteractor.currentScene.map {
+ sceneInteractor.isSceneInFamily(it, SceneFamilies.NotifShade) || it == Scenes.Lockscreen
+ }.dumpWhileCollecting("isScrollable")
/** Whether the notification stack is displayed in doze mode. */
val isDozing: Flow<Boolean> by lazy {
@@ -163,22 +168,4 @@
keyguardInteractor.get().isDozing.dumpWhileCollecting("isDozing")
}
}
-
- private val shadeMode: StateFlow<ShadeMode> = shadeInteractor.shadeMode
-
- private val notificationsScene: SceneKey
- get() =
- if (shadeMode.value is ShadeMode.Dual) {
- Scenes.NotificationsShade
- } else {
- Scenes.Shade
- }
-
- private val quickSettingsScene: SceneKey
- get() =
- if (shadeMode.value is ShadeMode.Dual) {
- Scenes.QuickSettingsShade
- } else {
- Scenes.QuickSettings
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index 6dfaec9..1fc2821 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -53,6 +53,7 @@
import com.android.systemui.keyguard.ui.viewmodel.GoneToAodTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.GoneToDozingTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.GoneToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToGlanceableHubTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToGoneTransitionViewModel
@@ -120,6 +121,7 @@
private val goneToAodTransitionViewModel: GoneToAodTransitionViewModel,
private val goneToDozingTransitionViewModel: GoneToDozingTransitionViewModel,
private val goneToDreamingTransitionViewModel: GoneToDreamingTransitionViewModel,
+ private val goneToLockscreenTransitionViewModel: GoneToLockscreenTransitionViewModel,
private val lockscreenToDreamingTransitionViewModel: LockscreenToDreamingTransitionViewModel,
private val lockscreenToGlanceableHubTransitionViewModel:
LockscreenToGlanceableHubTransitionViewModel,
@@ -441,7 +443,7 @@
anyOf(
*toFlowArray(statesForHiddenKeyguard) { state ->
keyguardTransitionInteractor
- .transitionStepsToState(state)
+ .transition(Edge.create(to = state))
.map { it.value > 0f && it.transitionState == RUNNING }
.onStart { emit(false) }
}
@@ -472,6 +474,9 @@
// All transition view models are mututally exclusive, and safe to merge
val alphaTransitions =
merge(
+ keyguardInteractor.dismissAlpha.dumpWhileCollecting(
+ "keyguardInteractor.dismissAlpha"
+ ),
alternateBouncerToGoneTransitionViewModel.notificationAlpha(viewState),
aodToGoneTransitionViewModel.notificationAlpha(viewState),
aodToLockscreenTransitionViewModel.notificationAlpha,
@@ -481,7 +486,8 @@
dreamingToLockscreenTransitionViewModel.lockscreenAlpha,
goneToAodTransitionViewModel.notificationAlpha,
goneToDreamingTransitionViewModel.lockscreenAlpha,
- goneToDozingTransitionViewModel.lockscreenAlpha,
+ goneToDozingTransitionViewModel.notificationAlpha,
+ goneToLockscreenTransitionViewModel.lockscreenAlpha,
lockscreenToDreamingTransitionViewModel.lockscreenAlpha,
lockscreenToGoneTransitionViewModel.notificationAlpha(viewState),
lockscreenToOccludedTransitionViewModel.lockscreenAlpha,
@@ -498,24 +504,10 @@
// These remaining cases handle alpha changes within an existing state, such as
// shade expansion or swipe to dismiss
combineTransform(
- isOnLockscreenWithoutShade,
isTransitioningToHiddenKeyguard,
- shadeCollapseFadeIn,
alphaForShadeAndQsExpansion,
- keyguardInteractor.dismissAlpha.dumpWhileCollecting(
- "keyguardInteractor.keyguardAlpha"
- ),
- ) {
- isOnLockscreenWithoutShade,
- isTransitioningToHiddenKeyguard,
- shadeCollapseFadeIn,
- alphaForShadeAndQsExpansion,
- dismissAlpha ->
- if (isOnLockscreenWithoutShade) {
- if (!shadeCollapseFadeIn && dismissAlpha != null) {
- emit(dismissAlpha)
- }
- } else if (!isTransitioningToHiddenKeyguard) {
+ ) { isTransitioningToHiddenKeyguard, alphaForShadeAndQsExpansion ->
+ if (!isTransitioningToHiddenKeyguard) {
emit(alphaForShadeAndQsExpansion)
}
},
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
index 2ab7aa9..fae0a46 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
@@ -85,6 +85,26 @@
)
}
+ override fun startPendingIntentWithoutDismissing(
+ intent: PendingIntent,
+ dismissShade: Boolean,
+ intentSentUiThreadCallback: Runnable?,
+ animationController: ActivityTransitionAnimator.Controller?,
+ fillInIntent: Intent?,
+ extraOptions: Bundle?
+ ) {
+ activityStarterInternal.startPendingIntentDismissingKeyguard(
+ intent = intent,
+ intentSentUiThreadCallback = intentSentUiThreadCallback,
+ animationController = animationController,
+ showOverLockscreen = true,
+ skipLockscreenChecks = true,
+ dismissShade = dismissShade,
+ fillInIntent = fillInIntent,
+ extraOptions = extraOptions,
+ )
+ }
+
override fun startPendingIntentMaybeDismissingKeyguard(
intent: PendingIntent,
intentSentUiThreadCallback: Runnable?,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt
index c9becb4..cff9f5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt
@@ -39,6 +39,7 @@
associatedView: View? = null,
animationController: ActivityTransitionAnimator.Controller? = null,
showOverLockscreen: Boolean = false,
+ skipLockscreenChecks: Boolean = false,
fillInIntent: Intent? = null,
extraOptions: Bundle? = null,
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
index e580f64..dbb95e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
@@ -40,6 +40,7 @@
associatedView: View?,
animationController: ActivityTransitionAnimator.Controller?,
showOverLockscreen: Boolean,
+ skipLockscreenChecks: Boolean,
fillInIntent: Intent?,
extraOptions: Bundle?
) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index f83aed8..97b6f95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -538,6 +538,9 @@
// later to awaken.
}
mNotificationShadeWindowController.setNotificationShadeFocusable(false);
+ // Notify the interactor first, to prevent race conditions with the screen waking up
+ // that would show a flicker of the lockscreen on DOZING->GONE
+ mBiometricUnlockInteractor.setBiometricUnlockState(mode, biometricUnlockSource);
mKeyguardViewMediator.onWakeAndUnlocking(wakeInKeyguard);
Trace.endSection();
break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index e44edcb..cd59d4e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -21,6 +21,7 @@
import static com.android.systemui.Flags.updateUserSwitcherBackground;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -46,6 +47,7 @@
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.logging.KeyguardLogger;
import com.android.systemui.battery.BatteryMeterViewController;
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.log.core.LogLevel;
@@ -83,6 +85,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
import javax.inject.Inject;
@@ -128,6 +131,7 @@
private final Executor mBackgroundExecutor;
private final Object mLock = new Object();
private final KeyguardLogger mLogger;
+ private final CommunalSceneInteractor mCommunalSceneInteractor;
private View mSystemIconsContainer;
private final StatusOverlayHoverListenerFactory mStatusOverlayHoverListenerFactory;
@@ -241,6 +245,12 @@
}
};
+ private boolean mCommunalShowing;
+
+ private final Consumer<Boolean> mCommunalConsumer = (communalShowing) -> {
+ mCommunalShowing = communalShowing;
+ updateViewState();
+ };
private final DisableStateTracker mDisableStateTracker;
@@ -298,7 +308,8 @@
@Main Executor mainExecutor,
@Background Executor backgroundExecutor,
KeyguardLogger logger,
- StatusOverlayHoverListenerFactory statusOverlayHoverListenerFactory
+ StatusOverlayHoverListenerFactory statusOverlayHoverListenerFactory,
+ CommunalSceneInteractor communalSceneInteractor
) {
super(view);
mCarrierTextController = carrierTextController;
@@ -324,6 +335,7 @@
mMainExecutor = mainExecutor;
mBackgroundExecutor = backgroundExecutor;
mLogger = logger;
+ mCommunalSceneInteractor = communalSceneInteractor;
mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
mKeyguardStateController.addCallback(
@@ -405,6 +417,7 @@
UserHandle.USER_ALL);
updateUserSwitcher();
onThemeChanged();
+ collectFlow(mView, mCommunalSceneInteractor.isCommunalVisible(), mCommunalConsumer);
}
@Override
@@ -559,6 +572,7 @@
&& !mDozing
&& !hideForBypass
&& !mDisableStateTracker.isDisabled()
+ && !mCommunalShowing
? View.VISIBLE : View.INVISIBLE;
updateViewState(newAlpha, newVisibility);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
index fc29eab..e400ab6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
@@ -228,6 +228,7 @@
associatedView: View?,
animationController: ActivityTransitionAnimator.Controller?,
showOverLockscreen: Boolean,
+ skipLockscreenChecks: Boolean,
fillInIntent: Intent?,
extraOptions: Bundle?,
) {
@@ -246,10 +247,10 @@
val actuallyShowOverLockscreen =
showOverLockscreen &&
intent.isActivity &&
- activityIntentHelper.wouldPendingShowOverLockscreen(
+ (skipLockscreenChecks || activityIntentHelper.wouldPendingShowOverLockscreen(
intent,
lockScreenUserManager.currentUserId
- )
+ ))
val animate =
!willLaunchResolverActivity &&
@@ -469,7 +470,7 @@
shadeControllerLazy.get().collapseShadeForActivityStart()
}
if (communalHub()) {
- communalSceneInteractor.snapToScene(CommunalScenes.Blank)
+ communalSceneInteractor.snapToSceneForActivityStart(CommunalScenes.Blank)
}
return deferred
}
@@ -555,7 +556,12 @@
override fun onTransitionAnimationStart(isExpandingFullyAbove: Boolean) {
super.onTransitionAnimationStart(isExpandingFullyAbove)
-
+ if (communalHub()) {
+ communalSceneInteractor.snapToSceneForActivityStart(
+ CommunalScenes.Blank,
+ ActivityTransitionAnimator.TIMINGS.totalDuration
+ )
+ }
// Double check that the keyguard is still showing and not going
// away, but if so set the keyguard occluded. Typically, WM will let
// KeyguardViewMediator know directly, but we're overriding that to
@@ -581,9 +587,6 @@
// collapse the shade (or at least run the post collapse runnables)
// later on.
centralSurfaces?.setIsLaunchingActivityOverLockscreen(false, false)
- if (communalHub()) {
- communalSceneInteractor.snapToScene(CommunalScenes.Blank)
- }
delegate.onTransitionAnimationEnd(isExpandingFullyAbove)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index db4f0af..b40bf56 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -73,6 +73,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor;
import com.android.systemui.keyguard.shared.model.DismissAction;
+import com.android.systemui.keyguard.shared.model.Edge;
import com.android.systemui.keyguard.shared.model.KeyguardDone;
import com.android.systemui.keyguard.shared.model.KeyguardState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
@@ -508,8 +509,8 @@
mListenForCanShowAlternateBouncer = null;
if (!DeviceEntryUdfpsRefactor.isEnabled()) {
mListenForAlternateBouncerTransitionSteps = mJavaAdapter.alwaysCollectFlow(
- mKeyguardTransitionInteractor.transitionStepsFromState(
- KeyguardState.ALTERNATE_BOUNCER),
+ mKeyguardTransitionInteractor
+ .transition(Edge.create(KeyguardState.ALTERNATE_BOUNCER)),
this::consumeFromAlternateBouncerTransitionSteps
);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index a7d4ce3..d128057 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -29,35 +29,36 @@
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.CoreStartable
import com.android.systemui.Dumpable
-import com.android.systemui.res.R
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.statusbar.chips.ui.view.ChipChronometer
+import com.android.systemui.res.R
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
+import com.android.systemui.statusbar.chips.ui.view.ChipChronometer
import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.statusbar.phone.ongoingcall.data.model.OngoingCallModel
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.util.time.SystemClock
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.launch
import java.io.PrintWriter
import java.util.concurrent.Executor
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
-/**
- * A controller to handle the ongoing call chip in the collapsed status bar.
- */
+/** A controller to handle the ongoing call chip in the collapsed status bar. */
@SysUISingleton
-class OngoingCallController @Inject constructor(
+class OngoingCallController
+@Inject
+constructor(
@Application private val scope: CoroutineScope,
private val context: Context,
private val ongoingCallRepository: OngoingCallRepository,
@@ -79,54 +80,61 @@
private val mListeners: MutableList<OngoingCallListener> = mutableListOf()
private val uidObserver = CallAppUidObserver()
- private val notifListener = object : NotifCollectionListener {
- // Temporary workaround for b/178406514 for testing purposes.
- //
- // b/178406514 means that posting an incoming call notif then updating it to an ongoing call
- // notif does not work (SysUI never receives the update). This workaround allows us to
- // trigger the ongoing call chip when an ongoing call notif is *added* rather than
- // *updated*, allowing us to test the chip.
- //
- // TODO(b/183229367): Remove this function override when b/178406514 is fixed.
- override fun onEntryAdded(entry: NotificationEntry) {
- onEntryUpdated(entry, true)
- }
+ private val notifListener =
+ object : NotifCollectionListener {
+ // Temporary workaround for b/178406514 for testing purposes.
+ //
+ // b/178406514 means that posting an incoming call notif then updating it to an ongoing
+ // call notif does not work (SysUI never receives the update). This workaround allows us
+ // to trigger the ongoing call chip when an ongoing call notif is *added* rather than
+ // *updated*, allowing us to test the chip.
+ //
+ // TODO(b/183229367): Remove this function override when b/178406514 is fixed.
+ override fun onEntryAdded(entry: NotificationEntry) {
+ onEntryUpdated(entry, true)
+ }
- override fun onEntryUpdated(entry: NotificationEntry) {
- // We have a new call notification or our existing call notification has been updated.
- // TODO(b/183229367): This likely won't work if you take a call from one app then
- // switch to a call from another app.
- if (callNotificationInfo == null && isCallNotification(entry) ||
- (entry.sbn.key == callNotificationInfo?.key)) {
- val newOngoingCallInfo = CallNotificationInfo(
- entry.sbn.key,
- entry.sbn.notification.getWhen(),
- entry.sbn.notification.contentIntent,
- entry.sbn.uid,
- entry.sbn.notification.extras.getInt(
- Notification.EXTRA_CALL_TYPE, -1) == CALL_TYPE_ONGOING,
- statusBarSwipedAway = callNotificationInfo?.statusBarSwipedAway ?: false
- )
- if (newOngoingCallInfo == callNotificationInfo) {
- return
+ override fun onEntryUpdated(entry: NotificationEntry) {
+ // We have a new call notification or our existing call notification has been
+ // updated.
+ // TODO(b/183229367): This likely won't work if you take a call from one app then
+ // switch to a call from another app.
+ if (
+ callNotificationInfo == null && isCallNotification(entry) ||
+ (entry.sbn.key == callNotificationInfo?.key)
+ ) {
+ val newOngoingCallInfo =
+ CallNotificationInfo(
+ entry.sbn.key,
+ entry.sbn.notification.getWhen(),
+ entry.sbn.notification.contentIntent,
+ entry.sbn.uid,
+ entry.sbn.notification.extras.getInt(
+ Notification.EXTRA_CALL_TYPE,
+ -1
+ ) == CALL_TYPE_ONGOING,
+ statusBarSwipedAway = callNotificationInfo?.statusBarSwipedAway ?: false
+ )
+ if (newOngoingCallInfo == callNotificationInfo) {
+ return
+ }
+
+ callNotificationInfo = newOngoingCallInfo
+ if (newOngoingCallInfo.isOngoing) {
+ updateChip()
+ } else {
+ removeChip()
+ }
}
+ }
- callNotificationInfo = newOngoingCallInfo
- if (newOngoingCallInfo.isOngoing) {
- updateChip()
- } else {
+ override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
+ if (entry.sbn.key == callNotificationInfo?.key) {
removeChip()
}
}
}
- override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
- if (entry.sbn.key == callNotificationInfo?.key) {
- removeChip()
- }
- }
- }
-
override fun start() {
dumpManager.registerDumpable(this)
notifCollection.addCollectionListener(notifListener)
@@ -169,8 +177,21 @@
*/
fun hasOngoingCall(): Boolean {
return callNotificationInfo?.isOngoing == true &&
- // When the user is in the phone app, don't show the chip.
- !uidObserver.isCallAppVisible
+ // When the user is in the phone app, don't show the chip.
+ !uidObserver.isCallAppVisible
+ }
+
+ /** Creates the right [OngoingCallModel] based on the call state. */
+ private fun getOngoingCallModel(): OngoingCallModel {
+ if (hasOngoingCall()) {
+ val currentInfo =
+ callNotificationInfo
+ // This shouldn't happen, but protect against it in case
+ ?: return OngoingCallModel.NoCall
+ return OngoingCallModel.InCall(currentInfo.callStartTime)
+ } else {
+ return OngoingCallModel.NoCall
+ }
}
override fun addCallback(listener: OngoingCallListener) {
@@ -182,9 +203,7 @@
}
override fun removeCallback(listener: OngoingCallListener) {
- synchronized(mListeners) {
- mListeners.remove(listener)
- }
+ synchronized(mListeners) { mListeners.remove(listener) }
}
private fun updateChip() {
@@ -196,8 +215,8 @@
if (currentChipView != null && timeView != null) {
if (currentCallNotificationInfo.hasValidStartTime()) {
timeView.setShouldHideText(false)
- timeView.base = currentCallNotificationInfo.callStartTime -
- systemClock.currentTimeMillis() +
+ timeView.base =
+ currentCallNotificationInfo.callStartTime - systemClock.currentTimeMillis() +
systemClock.elapsedRealtime()
timeView.start()
} else {
@@ -218,14 +237,19 @@
callNotificationInfo = null
if (DEBUG) {
- Log.w(TAG, "Ongoing call chip view could not be found; " +
- "Not displaying chip in status bar")
+ Log.w(
+ TAG,
+ "Ongoing call chip view could not be found; " +
+ "Not displaying chip in status bar"
+ )
}
}
}
private fun updateChipClickListener() {
- if (callNotificationInfo == null) { return }
+ if (callNotificationInfo == null) {
+ return
+ }
val currentChipView = chipView
val backgroundView =
currentChipView?.findViewById<View>(R.id.ongoing_activity_chip_background)
@@ -237,7 +261,8 @@
intent,
ActivityTransitionAnimator.Controller.fromView(
backgroundView,
- InteractionJankMonitor.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP)
+ InteractionJankMonitor.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
+ )
)
}
}
@@ -249,9 +274,11 @@
}
private fun updateGestureListening() {
- if (callNotificationInfo == null ||
- callNotificationInfo?.statusBarSwipedAway == true ||
- !isFullscreen) {
+ if (
+ callNotificationInfo == null ||
+ callNotificationInfo?.statusBarSwipedAway == true ||
+ !isFullscreen
+ ) {
swipeStatusBarAwayGestureHandler.removeOnGestureDetectedCallback(TAG)
} else {
swipeStatusBarAwayGestureHandler.addOnGestureDetectedCallback(TAG) { _ ->
@@ -270,30 +297,31 @@
}
/** Tear down anything related to the chip view to prevent leaks. */
- @VisibleForTesting
- fun tearDownChipView() = chipView?.getTimeView()?.stop()
+ @VisibleForTesting fun tearDownChipView() = chipView?.getTimeView()?.stop()
private fun View.getTimeView(): ChipChronometer? {
return this.findViewById(R.id.ongoing_activity_chip_time)
}
/**
- * If there's an active ongoing call, then we will force the status bar to always show, even if
- * the user is in immersive mode. However, we also want to give users the ability to swipe away
- * the status bar if they need to access the area under the status bar.
- *
- * This method updates the status bar window appropriately when the swipe away gesture is
- * detected.
- */
+ * If there's an active ongoing call, then we will force the status bar to always show, even if
+ * the user is in immersive mode. However, we also want to give users the ability to swipe away
+ * the status bar if they need to access the area under the status bar.
+ *
+ * This method updates the status bar window appropriately when the swipe away gesture is
+ * detected.
+ */
private fun onSwipeAwayGestureDetected() {
- if (DEBUG) { Log.d(TAG, "Swipe away gesture detected") }
+ if (DEBUG) {
+ Log.d(TAG, "Swipe away gesture detected")
+ }
callNotificationInfo = callNotificationInfo?.copy(statusBarSwipedAway = true)
statusBarWindowController.setOngoingProcessRequiresStatusBarVisible(false)
swipeStatusBarAwayGestureHandler.removeOnGestureDetectedCallback(TAG)
}
private fun sendStateChangeEvent() {
- ongoingCallRepository.setHasOngoingCall(hasOngoingCall())
+ ongoingCallRepository.setOngoingCallState(getOngoingCallModel())
mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
}
@@ -308,8 +336,8 @@
val statusBarSwipedAway: Boolean
) {
/**
- * Returns true if the notification information has a valid call start time.
- * See b/192379214.
+ * Returns true if the notification information has a valid call start time. See
+ * b/192379214.
*/
fun hasValidStartTime(): Boolean = callStartTime > 0
}
@@ -342,9 +370,10 @@
callAppUid = uid
try {
- isCallAppVisible = isProcessVisibleToUser(
- iActivityManager.getUidProcessState(uid, context.opPackageName)
- )
+ isCallAppVisible =
+ isProcessVisibleToUser(
+ iActivityManager.getUidProcessState(uid, context.opPackageName)
+ )
if (isRegistered) {
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/model/OngoingCallModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/model/OngoingCallModel.kt
new file mode 100644
index 0000000..aaa52a7b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/model/OngoingCallModel.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone.ongoingcall.data.model
+
+/** Represents the state of any ongoing calls. */
+sealed interface OngoingCallModel {
+ /** There is no ongoing call. */
+ data object NoCall : OngoingCallModel
+
+ /**
+ * There *is* an ongoing call.
+ *
+ * @property startTimeMs the time that the phone call started, based on the notification's
+ * `when` field. Importantly, this time is relative to
+ * [com.android.systemui.util.time.SystemClock.currentTimeMillis], **not**
+ * [com.android.systemui.util.time.SystemClock.elapsedRealtime].
+ */
+ data class InCall(val startTimeMs: Long) : OngoingCallModel
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt
index 886481e..554c474 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone.ongoingcall.data.repository
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.phone.ongoingcall.data.model.OngoingCallModel
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -32,15 +33,15 @@
*/
@SysUISingleton
class OngoingCallRepository @Inject constructor() {
- private val _hasOngoingCall = MutableStateFlow(false)
- /** True if there's currently an ongoing call notification and false otherwise. */
- val hasOngoingCall: StateFlow<Boolean> = _hasOngoingCall.asStateFlow()
+ private val _ongoingCallState = MutableStateFlow<OngoingCallModel>(OngoingCallModel.NoCall)
+ /** The current ongoing call state. */
+ val ongoingCallState: StateFlow<OngoingCallModel> = _ongoingCallState.asStateFlow()
/**
- * Sets whether there's currently an ongoing call notification. Should only be set from
+ * Sets the current ongoing call state, based on notifications. Should only be set from
* [com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController].
*/
- fun setHasOngoingCall(hasOngoingCall: Boolean) {
- _hasOngoingCall.value = hasOngoingCall
+ fun setOngoingCallState(state: OngoingCallModel) {
+ _ongoingCallState.value = state
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfig.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfig.kt
index f4e3eab..0871c86 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfig.kt
@@ -18,6 +18,7 @@
import android.os.PersistableBundle
import android.telephony.CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL
+import android.telephony.CarrierConfigManager.KEY_SHOW_5G_SLICE_ICON_BOOL
import android.telephony.CarrierConfigManager.KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL
import androidx.annotation.VisibleForTesting
import kotlinx.coroutines.flow.MutableStateFlow
@@ -66,10 +67,16 @@
/** Flow tracking the [KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL] config */
val showOperatorNameInStatusBar: StateFlow<Boolean> = showOperatorName.config
+ private val showNetworkSlice =
+ BooleanCarrierConfig(KEY_SHOW_5G_SLICE_ICON_BOOL, defaultConfig)
+ /** Flow tracking the [KEY_SHOW_5G_SLICE_ICON_BOOL] config */
+ val allowNetworkSliceIndicator: StateFlow<Boolean> = showNetworkSlice.config
+
private val trackedConfigs =
listOf(
inflateSignalStrength,
showOperatorName,
+ showNetworkSlice,
)
/** Ingest a new carrier config, and switch all of the tracked keys over to the new values */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
index 425c58b..205205e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
@@ -48,6 +48,9 @@
/** Reflects the value from the carrier config INFLATE_SIGNAL_STRENGTH for this connection */
val inflateSignalStrength: StateFlow<Boolean>
+ /** Carrier config KEY_SHOW_5G_SLICE_ICON_BOOL for this connection */
+ val allowNetworkSliceIndicator: StateFlow<Boolean>
+
/**
* The table log buffer created for this connection. Will have the name "MobileConnectionLog
* [subId]"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt
index 83d5f2b..3261b71 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt
@@ -79,6 +79,9 @@
)
.stateIn(scope, SharingStarted.WhileSubscribed(), _inflateSignalStrength.value)
+ // I don't see a reason why we would turn the config off for demo mode.
+ override val allowNetworkSliceIndicator = MutableStateFlow(true)
+
private val _isEmergencyOnly = MutableStateFlow(false)
override val isEmergencyOnly =
_isEmergencyOnly
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
index a532e62..2e47678 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
@@ -166,6 +166,7 @@
override val isRoaming = MutableStateFlow(false).asStateFlow()
override val carrierId = MutableStateFlow(INVALID_SUBSCRIPTION_ID).asStateFlow()
override val inflateSignalStrength = MutableStateFlow(false).asStateFlow()
+ override val allowNetworkSliceIndicator = MutableStateFlow(false).asStateFlow()
override val isEmergencyOnly = MutableStateFlow(false).asStateFlow()
override val operatorAlphaShort = MutableStateFlow(null).asStateFlow()
override val isInService = MutableStateFlow(true).asStateFlow()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
index 41559b2..a5e47a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
@@ -308,6 +308,21 @@
activeRepo.value.inflateSignalStrength.value
)
+ override val allowNetworkSliceIndicator =
+ activeRepo
+ .flatMapLatest { it.allowNetworkSliceIndicator }
+ .logDiffsForTable(
+ tableLogBuffer,
+ columnPrefix = "",
+ columnName = "allowSlice",
+ initialValue = activeRepo.value.allowNetworkSliceIndicator.value,
+ )
+ .stateIn(
+ scope,
+ SharingStarted.WhileSubscribed(),
+ activeRepo.value.allowNetworkSliceIndicator.value
+ )
+
override val numberOfLevels =
activeRepo
.flatMapLatest { it.numberOfLevels }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
index 6803a9d..9449659 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
@@ -310,6 +310,7 @@
.stateIn(scope, SharingStarted.WhileSubscribed(), UnknownNetworkType)
override val inflateSignalStrength = systemUiCarrierConfig.shouldInflateSignalStrength
+ override val allowNetworkSliceIndicator = systemUiCarrierConfig.allowNetworkSliceIndicator
override val numberOfLevels =
inflateSignalStrength
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
index ed9e405..507759c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
@@ -253,7 +253,13 @@
)
override val showSliceAttribution: StateFlow<Boolean> =
- connectionRepository.hasPrioritizedNetworkCapabilities
+ combine(
+ connectionRepository.allowNetworkSliceIndicator,
+ connectionRepository.hasPrioritizedNetworkCapabilities,
+ ) { allowed, hasPrioritizedNetworkCapabilities ->
+ allowed && hasPrioritizedNetworkCapabilities
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), false)
override val isNonTerrestrial: StateFlow<Boolean> =
if (Flags.carrierEnabledSatelliteFlag()) {
@@ -350,7 +356,8 @@
shownLevel.map {
SignalIconModel.Satellite(
level = it,
- icon = SatelliteIconModel.fromSignalStrength(it)
+ icon =
+ SatelliteIconModel.fromSignalStrength(it)
?: SatelliteIconModel.fromSignalStrength(0)!!
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepository.kt
index d38e834..1d08f2b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepository.kt
@@ -25,6 +25,9 @@
* given mobile data subscription.
*/
interface DeviceBasedSatelliteRepository {
+ /** The current status of satellite provisioning. If not false, we don't want to show an icon */
+ val isSatelliteProvisioned: StateFlow<Boolean>
+
/** See [SatelliteConnectionState] for available states */
val connectionState: StateFlow<SatelliteConnectionState>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcher.kt
index 6b1bc65..58c30e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcher.kt
@@ -97,6 +97,11 @@
}
.stateIn(scope, SharingStarted.WhileSubscribed(), realImpl)
+ override val isSatelliteProvisioned: StateFlow<Boolean> =
+ activeRepo
+ .flatMapLatest { it.isSatelliteProvisioned }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), realImpl.isSatelliteProvisioned.value)
+
override val connectionState: StateFlow<SatelliteConnectionState> =
activeRepo
.flatMapLatest { it.connectionState }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepository.kt
index 56034f0..6ad295e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepository.kt
@@ -36,6 +36,7 @@
) : DeviceBasedSatelliteRepository {
private var demoCommandJob: Job? = null
+ override val isSatelliteProvisioned = MutableStateFlow(true)
override val connectionState = MutableStateFlow(SatelliteConnectionState.Unknown)
override val signalStrength = MutableStateFlow(0)
override val isSatelliteAllowedForCurrentLocation = MutableStateFlow(true)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
index 1449e53..ec3af87 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
@@ -23,6 +23,7 @@
import android.telephony.satellite.SatelliteManager
import android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS
import android.telephony.satellite.SatelliteModemStateCallback
+import android.telephony.satellite.SatelliteProvisionStateCallback
import android.telephony.satellite.SatelliteSupportedStateCallback
import androidx.annotation.VisibleForTesting
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
@@ -337,6 +338,43 @@
}
}
+ override val isSatelliteProvisioned: StateFlow<Boolean> =
+ satelliteSupport
+ .whenSupported(
+ supported = ::satelliteProvisioned,
+ orElse = flowOf(false),
+ retrySignal = telephonyProcessCrashedEvent,
+ )
+ .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
+ private fun satelliteProvisioned(sm: SupportedSatelliteManager): Flow<Boolean> =
+ conflatedCallbackFlow {
+ val callback = SatelliteProvisionStateCallback { provisioned ->
+ logBuffer.i {
+ "onSatelliteProvisionStateChanged: " +
+ if (provisioned) "provisioned" else "not provisioned"
+ }
+ trySend(provisioned)
+ }
+
+ var registered = false
+ try {
+ sm.registerForProvisionStateChanged(
+ bgDispatcher.asExecutor(),
+ callback,
+ )
+ registered = true
+ } catch (e: Exception) {
+ logBuffer.e("error registering for provisioning state callback", e)
+ }
+
+ awaitClose {
+ if (registered) {
+ sm.unregisterForProvisionStateChanged(callback)
+ }
+ }
+ }
+
/**
* Signal that we should start polling [checkIsSatelliteAllowed]. We only need to poll if there
* are active listeners to [isSatelliteAllowedForCurrentLocation]
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
index b66ace6..03f88c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
@@ -27,7 +27,6 @@
import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
-import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -45,7 +44,6 @@
constructor(
val repo: DeviceBasedSatelliteRepository,
iconsInteractor: MobileIconsInteractor,
- deviceProvisioningInteractor: DeviceProvisioningInteractor,
wifiInteractor: WifiInteractor,
@Application scope: CoroutineScope,
@DeviceBasedSatelliteInputLog private val logBuffer: LogBuffer,
@@ -78,7 +76,7 @@
}
.stateIn(scope, SharingStarted.WhileSubscribed(), 0)
- val isDeviceProvisioned: Flow<Boolean> = deviceProvisioningInteractor.isDeviceProvisioned
+ val isSatelliteProvisioned = repo.isSatelliteProvisioned
val isWifiActive: Flow<Boolean> =
wifiInteractor.wifiNetwork.map { it is WifiNetworkModel.Active }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
index 0ed1b9b..48278d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
@@ -79,11 +79,11 @@
} else {
combine(
interactor.isSatelliteAllowed,
- interactor.isDeviceProvisioned,
+ interactor.isSatelliteProvisioned,
interactor.isWifiActive,
airplaneModeRepository.isAirplaneMode
- ) { isSatelliteAllowed, isDeviceProvisioned, isWifiActive, isAirplaneMode ->
- isSatelliteAllowed && isDeviceProvisioned && !isWifiActive && !isAirplaneMode
+ ) { isSatelliteAllowed, isSatelliteProvisioned, isWifiActive, isAirplaneMode ->
+ isSatelliteAllowed && isSatelliteProvisioned && !isWifiActive && !isAirplaneMode
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
index a2ec1f2..44b5baf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
@@ -94,7 +94,7 @@
is OngoingActivityChipModel.Shown -> {
IconViewBinder.bind(chipModel.icon, chipIconView)
ChipChronometerBinder.bind(chipModel.startTimeMs, chipTimeView)
- // TODO(b/332662551): Attach click listener to chip
+ chipView.setOnClickListener(chipModel.onClickListener)
listener.onOngoingActivityStatusChanged(
hasOngoingActivity = true
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
index a972985..32774e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
@@ -35,10 +35,7 @@
@SysUISingleton
class AvalancheController
@Inject
-constructor(
- dumpManager: DumpManager,
- private val uiEventLogger: UiEventLogger
-) : Dumpable {
+constructor(dumpManager: DumpManager, private val uiEventLogger: UiEventLogger) : Dumpable {
private val tag = "AvalancheController"
private val debug = Compile.IS_DEBUG && Log.isLoggable(tag, Log.DEBUG)
@@ -69,14 +66,11 @@
@VisibleForTesting var debugDropSet: MutableSet<HeadsUpEntry> = HashSet()
enum class ThrottleEvent(private val id: Int) : UiEventLogger.UiEventEnum {
- @UiEvent(doc = "HUN was shown.")
- SHOWN(1812),
-
+ @UiEvent(doc = "HUN was shown.") AVALANCHE_THROTTLING_HUN_SHOWN(1821),
@UiEvent(doc = "HUN was dropped to show higher priority HUNs.")
- DROPPED(1813),
-
+ AVALANCHE_THROTTLING_HUN_DROPPED(1822),
@UiEvent(doc = "HUN was removed while waiting to show.")
- REMOVED(1814);
+ AVALANCHE_THROTTLING_HUN_REMOVED(1823);
override fun getId(): Int {
return id
@@ -97,7 +91,7 @@
runnable.run()
return
}
- log { "\n "}
+ log { "\n " }
val fn = "$label => AvalancheController.update ${getKey(entry)}"
if (entry == null) {
log { "Entry is NULL, stop update." }
@@ -129,9 +123,10 @@
// HeadsUpEntry.updateEntry recursively calls AvalancheController#update
// and goes to the isShowing case above
headsUpEntryShowing!!.updateEntry(
- /* updatePostTime= */ false,
- /* updateEarliestRemovalTime= */ false,
- /* reason= */ "avalanche duration update")
+ /* updatePostTime= */ false,
+ /* updateEarliestRemovalTime= */ false,
+ /* reason= */ "avalanche duration update"
+ )
}
}
logState("after $fn")
@@ -152,7 +147,7 @@
runnable.run()
return
}
- log { "\n "}
+ log { "\n " }
val fn = "$label => AvalancheController.delete " + getKey(entry)
if (entry == null) {
log { "$fn => entry NULL, running runnable" }
@@ -163,7 +158,7 @@
log { "$fn => remove from next" }
if (entry in nextMap) nextMap.remove(entry)
if (entry in nextList) nextList.remove(entry)
- uiEventLogger.log(ThrottleEvent.REMOVED)
+ uiEventLogger.log(ThrottleEvent.AVALANCHE_THROTTLING_HUN_REMOVED)
} else if (entry in debugDropSet) {
log { "$fn => remove from dropset" }
debugDropSet.remove(entry)
@@ -287,7 +282,7 @@
private fun showNow(entry: HeadsUpEntry, runnableList: MutableList<Runnable>) {
log { "SHOW: " + getKey(entry) }
- uiEventLogger.log(ThrottleEvent.SHOWN)
+ uiEventLogger.log(ThrottleEvent.AVALANCHE_THROTTLING_HUN_SHOWN)
headsUpEntryShowing = entry
runnableList.forEach {
@@ -318,7 +313,7 @@
// Log dropped HUNs
for (e in listToDrop) {
- uiEventLogger.log(ThrottleEvent.DROPPED)
+ uiEventLogger.log(ThrottleEvent.AVALANCHE_THROTTLING_HUN_DROPPED)
}
if (debug) {
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/TouchpadTutorialViewModel.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/TouchpadTutorialViewModel.kt
index 7669524..11740a8 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/TouchpadTutorialViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/TouchpadTutorialViewModel.kt
@@ -27,6 +27,10 @@
private val _screen = MutableStateFlow(Screen.TUTORIAL_SELECTION)
val screen: StateFlow<Screen> = _screen
+ fun goTo(screen: Screen) {
+ _screen.value = screen
+ }
+
class Factory @Inject constructor() : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/TutorialSelectionViewModel.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/TutorialSelectionViewModel.kt
deleted file mode 100644
index 1a8272d8..0000000
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/TutorialSelectionViewModel.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.touchpad.tutorial.ui
-
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.ViewModelProvider
-
-class TutorialSelectionViewModel : ViewModel()
-
-class TutorialSelectionViewModelFactory : ViewModelProvider.Factory {
-
- @Suppress("UNCHECKED_CAST")
- override fun <T : ViewModel> create(modelClass: Class<T>): T {
- return TutorialSelectionViewModel() as T
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt
index 09dd909..b7629c7 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt
@@ -19,6 +19,7 @@
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.lifecycle.Lifecycle.State.STARTED
@@ -33,8 +34,6 @@
import com.android.systemui.touchpad.tutorial.ui.Screen.HOME_GESTURE
import com.android.systemui.touchpad.tutorial.ui.Screen.TUTORIAL_SELECTION
import com.android.systemui.touchpad.tutorial.ui.TouchpadTutorialViewModel
-import com.android.systemui.touchpad.tutorial.ui.TutorialSelectionViewModel
-import com.android.systemui.touchpad.tutorial.ui.TutorialSelectionViewModelFactory
import javax.inject.Inject
class TouchpadTutorialActivity
@@ -45,27 +44,31 @@
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setContent { PlatformTheme { TouchpadTutorialScreen(viewModelFactory) } }
+ enableEdgeToEdge()
+ setContent {
+ PlatformTheme { TouchpadTutorialScreen(viewModelFactory, closeTutorial = { finish() }) }
+ }
}
}
@Composable
-fun TouchpadTutorialScreen(viewModelFactory: ViewModelProvider.Factory) {
+fun TouchpadTutorialScreen(viewModelFactory: ViewModelProvider.Factory, closeTutorial: () -> Unit) {
val vm = viewModel<TouchpadTutorialViewModel>(factory = viewModelFactory)
val activeScreen by vm.screen.collectAsStateWithLifecycle(STARTED)
when (activeScreen) {
- TUTORIAL_SELECTION -> TutorialSelectionScreen()
+ TUTORIAL_SELECTION ->
+ TutorialSelectionScreen(
+ onBackTutorialClicked = { vm.goTo(BACK_GESTURE) },
+ onHomeTutorialClicked = { vm.goTo(HOME_GESTURE) },
+ onActionKeyTutorialClicked = {},
+ onDoneButtonClicked = closeTutorial
+ )
BACK_GESTURE -> BackGestureTutorialScreen()
HOME_GESTURE -> HomeGestureTutorialScreen()
}
}
@Composable
-fun TutorialSelectionScreen() {
- val vm = viewModel<TutorialSelectionViewModel>(factory = TutorialSelectionViewModelFactory())
-}
-
-@Composable
fun BackGestureTutorialScreen() {
val vm = viewModel<BackGestureTutorialViewModel>(factory = GestureViewModelFactory())
}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TutorialSelectionScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TutorialSelectionScreen.kt
new file mode 100644
index 0000000..532eb1b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TutorialSelectionScreen.kt
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.touchpad.tutorial.ui.view
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import com.android.systemui.res.R
+
+@Composable
+fun TutorialSelectionScreen(
+ onBackTutorialClicked: () -> Unit,
+ onHomeTutorialClicked: () -> Unit,
+ onActionKeyTutorialClicked: () -> Unit,
+ onDoneButtonClicked: () -> Unit,
+) {
+ Column(
+ verticalArrangement = Arrangement.Center,
+ modifier =
+ Modifier.background(
+ color = MaterialTheme.colorScheme.surfaceContainer,
+ )
+ .fillMaxSize()
+ ) {
+ TutorialSelectionButtons(
+ onBackTutorialClicked = onBackTutorialClicked,
+ onHomeTutorialClicked = onHomeTutorialClicked,
+ onActionKeyTutorialClicked = onActionKeyTutorialClicked,
+ modifier = Modifier.padding(60.dp)
+ )
+ DoneButton(
+ onDoneButtonClicked = onDoneButtonClicked,
+ modifier = Modifier.padding(horizontal = 60.dp)
+ )
+ }
+}
+
+@Composable
+private fun TutorialSelectionButtons(
+ onBackTutorialClicked: () -> Unit,
+ onHomeTutorialClicked: () -> Unit,
+ onActionKeyTutorialClicked: () -> Unit,
+ modifier: Modifier = Modifier
+) {
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(20.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = modifier
+ ) {
+ TutorialButton(
+ text = stringResource(R.string.touchpad_tutorial_back_gesture_button),
+ onClick = onBackTutorialClicked,
+ color = MaterialTheme.colorScheme.primary,
+ modifier = Modifier.weight(1f)
+ )
+ TutorialButton(
+ text = stringResource(R.string.touchpad_tutorial_home_gesture_button),
+ onClick = onHomeTutorialClicked,
+ color = MaterialTheme.colorScheme.secondary,
+ modifier = Modifier.weight(1f)
+ )
+ TutorialButton(
+ text = stringResource(R.string.touchpad_tutorial_action_key_button),
+ onClick = onActionKeyTutorialClicked,
+ color = MaterialTheme.colorScheme.tertiary,
+ modifier = Modifier.weight(1f)
+ )
+ }
+}
+
+@Composable
+private fun TutorialButton(
+ text: String,
+ onClick: () -> Unit,
+ color: Color,
+ modifier: Modifier = Modifier
+) {
+ Button(
+ onClick = onClick,
+ shape = RoundedCornerShape(16.dp),
+ colors = ButtonDefaults.buttonColors(containerColor = color),
+ modifier = modifier.aspectRatio(0.66f)
+ ) {
+ Text(text = text, style = MaterialTheme.typography.headlineLarge)
+ }
+}
+
+@Composable
+private fun DoneButton(onDoneButtonClicked: () -> Unit, modifier: Modifier = Modifier) {
+ Row(
+ horizontalArrangement = Arrangement.End,
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = modifier.fillMaxWidth()
+ ) {
+ Button(onClick = onDoneButtonClicked) {
+ Text(stringResource(R.string.touchpad_tutorial_done_button))
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
index 139ac7e..291903d 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
@@ -21,6 +21,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.shade.NotificationPanelUnfoldAnimationController
import com.android.systemui.statusbar.phone.StatusBarMoveFromCenterAnimationController
+import com.android.systemui.unfold.dagger.NaturalRotation
import com.android.systemui.unfold.dagger.UnfoldBg
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
@@ -36,6 +37,7 @@
import dagger.multibindings.IntoSet
import java.util.Optional
import javax.inject.Named
+import javax.inject.Qualifier
import javax.inject.Scope
@Scope @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class SysUIUnfoldScope
@@ -54,8 +56,17 @@
@Module(subcomponents = [SysUIUnfoldComponent::class])
class SysUIUnfoldModule {
+ /**
+ * Qualifier for dependencies bound in [com.android.systemui.unfold.SysUIUnfoldModule]
+ */
+ @Qualifier
+ @MustBeDocumented
+ @Retention(AnnotationRetention.RUNTIME)
+ annotation class BoundFromSysUiUnfoldModule
+
@Provides
@SysUISingleton
+ @BoundFromSysUiUnfoldModule
fun provideSysUIUnfoldComponent(
provider: Optional<UnfoldTransitionProgressProvider>,
rotationProvider: Optional<NaturalRotationUnfoldProgressProvider>,
@@ -98,6 +109,13 @@
abstract fun bindsFoldLightRevealOverlayAnimation(
anim: FoldLightRevealOverlayAnimation
): FullscreenLightRevealAnimation
+
+ @Binds
+ @NaturalRotation
+ @SysUIUnfoldScope
+ abstract fun bindNaturalRotationUnfoldProgressProvider(
+ provider: NaturalRotationUnfoldProgressProvider
+ ): UnfoldTransitionProgressProvider
}
@SysUIUnfoldScope
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/MapUtils.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/MapUtils.kt
index 41cd95b..8d202ac 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/MapUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/MapUtils.kt
@@ -30,3 +30,6 @@
}
return destination
}
+
+/** Returns a map with all entries containing `null` values removed. */
+fun <K, V> Map<K, V?>.filterValuesNotNull(): Map<K, V> = mapValuesNotNull { it.value }
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt
index 405b57a..d9a2e95 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt
@@ -19,19 +19,24 @@
import android.content.Context
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
class Utils {
companion object {
fun <A, B, C> toTriple(a: A, bc: Pair<B, C>) = Triple(a, bc.first, bc.second)
+
fun <A, B, C> toTriple(ab: Pair<A, B>, c: C) = Triple(ab.first, ab.second, c)
fun <A, B, C, D> toQuad(a: A, b: B, c: C, d: D) = Quad(a, b, c, d)
+
fun <A, B, C, D> toQuad(a: A, bcd: Triple<B, C, D>) =
Quad(a, bcd.first, bcd.second, bcd.third)
fun <A, B, C, D> toQuad(abc: Triple<A, B, C>, d: D) =
Quad(abc.first, abc.second, abc.third, d)
fun <A, B, C, D, E> toQuint(a: A, b: B, c: C, d: D, e: E) = Quint(a, b, c, d, e)
+
fun <A, B, C, D, E> toQuint(a: A, bcde: Quad<B, C, D, E>) =
Quint(a, bcde.first, bcde.second, bcde.third, bcde.fourth)
@@ -50,6 +55,14 @@
)
/**
+ * Samples the provided flow, performs a filter on the sampled value, then returns the
+ * original value.
+ */
+ fun <A, B> Flow<A>.sampleFilter(b: Flow<B>, predicate: (B) -> Boolean): Flow<A> {
+ return this.sample(b, ::Pair).filter { (_, b) -> predicate(b) }.map { (a, _) -> a }
+ }
+
+ /**
* Samples the provided flows, emitting a tuple of the original flow's value as well as each
* of the combined flows' values.
*
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index e613216..f457470 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -136,6 +136,7 @@
import com.android.systemui.util.RoundedCornerProgressDrawable;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.volume.domain.interactor.VolumePanelNavigationInteractor;
+import com.android.systemui.volume.panel.shared.flag.VolumePanelFlag;
import com.android.systemui.volume.ui.binder.VolumeDialogMenuIconBinder;
import com.android.systemui.volume.ui.navigation.VolumeNavigator;
@@ -313,6 +314,7 @@
private final VibratorHelper mVibratorHelper;
private final com.android.systemui.util.time.SystemClock mSystemClock;
private final VolumeDialogMenuIconBinder mVolumeDialogMenuIconBinder;
+ private final VolumePanelFlag mVolumePanelFlag;
public VolumeDialogImpl(
Context context,
@@ -328,6 +330,7 @@
CsdWarningDialog.Factory csdWarningDialogFactory,
DevicePostureController devicePostureController,
Looper looper,
+ VolumePanelFlag volumePanelFlag,
DumpManager dumpManager,
Lazy<SecureSettings> secureSettings,
VibratorHelper vibratorHelper,
@@ -366,6 +369,7 @@
mSecureSettings = secureSettings;
mVolumeDialogMenuIconBinder = volumeDialogMenuIconBinder;
mDialogTimeoutMillis = DIALOG_TIMEOUT_MILLIS;
+ mVolumePanelFlag = volumePanelFlag;
dumpManager.registerDumpable("VolumeDialogImpl", this);
@@ -1364,6 +1368,9 @@
}
private void updateODICaptionsH(boolean isServiceComponentEnabled, boolean fromTooltip) {
+ // don't show captions view when the new volume panel is enabled.
+ isServiceComponentEnabled =
+ isServiceComponentEnabled && !mVolumePanelFlag.canUseNewVolumePanel();
if (mODICaptionsView != null) {
mODICaptionsView.setVisibility(isServiceComponentEnabled ? VISIBLE : GONE);
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
index fd68bfb..f8ddc42 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -44,6 +44,7 @@
import com.android.systemui.volume.domain.interactor.VolumePanelNavigationInteractor;
import com.android.systemui.volume.panel.dagger.VolumePanelComponent;
import com.android.systemui.volume.panel.dagger.factory.VolumePanelComponentFactory;
+import com.android.systemui.volume.panel.shared.flag.VolumePanelFlag;
import com.android.systemui.volume.ui.binder.VolumeDialogMenuIconBinder;
import com.android.systemui.volume.ui.navigation.VolumeNavigator;
@@ -112,6 +113,7 @@
VolumeNavigator volumeNavigator,
CsdWarningDialog.Factory csdFactory,
DevicePostureController devicePostureController,
+ VolumePanelFlag volumePanelFlag,
DumpManager dumpManager,
Lazy<SecureSettings> secureSettings,
VibratorHelper vibratorHelper,
@@ -131,6 +133,7 @@
csdFactory,
devicePostureController,
Looper.getMainLooper(),
+ volumePanelFlag,
dumpManager,
secureSettings,
vibratorHelper,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt
index 6c6a1cc..324579d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt
@@ -57,7 +57,7 @@
.toViewModel(
isChecked = isEnabled is SpatialAudioEnabledModel.SpatialAudioEnabled,
isHeadTrackingAvailable =
- isAvailable is SpatialAudioAvailabilityModel.SpatialAudio,
+ isAvailable is SpatialAudioAvailabilityModel.HeadTracking,
)
.copy(label = context.getString(R.string.volume_panel_spatial_audio_title))
}
@@ -69,7 +69,7 @@
// head tracking availability means there are three possible states for the spatial
// audio: disabled, enabled regular, enabled with head tracking.
// Show popup in this case instead of a togglealbe button.
- it is SpatialAudioAvailabilityModel.SpatialAudio
+ it is SpatialAudioAvailabilityModel.HeadTracking
}
.stateIn(scope, SharingStarted.Eagerly, false)
diff --git a/packages/SystemUI/tests/goldens/doubleClick_swapSide.json b/packages/SystemUI/tests/goldens/doubleClick_swapSide.json
index 044ddbc..c4998a5 100644
--- a/packages/SystemUI/tests/goldens/doubleClick_swapSide.json
+++ b/packages/SystemUI/tests/goldens/doubleClick_swapSide.json
@@ -38,7 +38,7 @@
"y": 96
},
{
- "x": 45.008995,
+ "x": 45.009,
"y": 96
},
{
@@ -133,7 +133,7 @@
"y": 96
},
{
- "x": 156.13857,
+ "x": 156.13858,
"y": 96
},
{
@@ -141,7 +141,7 @@
"y": 96
},
{
- "x": 64.81257,
+ "x": 64.81259,
"y": 96
},
{
@@ -149,11 +149,11 @@
"y": 96
},
{
- "x": 24.443243,
+ "x": 24.443266,
"y": 96
},
{
- "x": 14.680362,
+ "x": 14.680339,
"y": 96
},
{
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 5702a8c..69a6f2a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -348,7 +348,8 @@
fun listenForTransitionToAodFromGone_updatesClockDozeAmountToOne() =
runBlocking(IMMEDIATE) {
val transitionStep = MutableStateFlow(TransitionStep())
- whenever(keyguardTransitionInteractor.transitionStepsToState(AOD))
+ whenever(keyguardTransitionInteractor
+ .transition(Edge.create(to = AOD)))
.thenReturn(transitionStep)
val job = underTest.listenForAnyStateToAodTransition(this)
@@ -369,7 +370,8 @@
fun listenForTransitionToLSFromOccluded_updatesClockDozeAmountToZero() =
runBlocking(IMMEDIATE) {
val transitionStep = MutableStateFlow(TransitionStep())
- whenever(keyguardTransitionInteractor.transitionStepsToState(LOCKSCREEN))
+ whenever(keyguardTransitionInteractor
+ .transition(Edge.create(to = LOCKSCREEN)))
.thenReturn(transitionStep)
val job = underTest.listenForAnyStateToLockscreenTransition(this)
@@ -390,7 +392,8 @@
fun listenForTransitionToAodFromLockscreen_neverUpdatesClockDozeAmount() =
runBlocking(IMMEDIATE) {
val transitionStep = MutableStateFlow(TransitionStep())
- whenever(keyguardTransitionInteractor.transitionStepsToState(AOD))
+ whenever(keyguardTransitionInteractor
+ .transition(Edge.create(to = AOD)))
.thenReturn(transitionStep)
val job = underTest.listenForAnyStateToAodTransition(this)
@@ -411,7 +414,8 @@
fun listenForAnyStateToLockscreenTransition_neverUpdatesClockDozeAmount() =
runBlocking(IMMEDIATE) {
val transitionStep = MutableStateFlow(TransitionStep())
- whenever(keyguardTransitionInteractor.transitionStepsToState(LOCKSCREEN))
+ whenever(keyguardTransitionInteractor
+ .transition(Edge.create(to = LOCKSCREEN)))
.thenReturn(transitionStep)
val job = underTest.listenForAnyStateToLockscreenTransition(this)
@@ -432,7 +436,8 @@
fun listenForAnyStateToDozingTransition_UpdatesClockDozeAmountToOne() =
runBlocking(IMMEDIATE) {
val transitionStep = MutableStateFlow(TransitionStep())
- whenever(keyguardTransitionInteractor.transitionStepsToState(DOZING))
+ whenever(keyguardTransitionInteractor
+ .transition(Edge.create(to = DOZING)))
.thenReturn(transitionStep)
val job = underTest.listenForAnyStateToDozingTransition(this)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
index 3afca59..336183d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
@@ -18,26 +18,25 @@
import android.testing.AndroidTestingRunner
import android.view.View
-import android.view.ViewGroup
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.kosmos.Kosmos
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.res.R
+import com.android.systemui.shade.NotificationShadeWindowView
import com.android.systemui.statusbar.StatusBarState.KEYGUARD
import com.android.systemui.statusbar.StatusBarState.SHADE
+import com.android.systemui.unfold.FakeUnfoldTransitionProvider
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
-import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
-import com.android.systemui.util.mockito.capture
-import com.android.systemui.util.mockito.whenever
+import com.android.systemui.unfold.fakeUnfoldTransitionProgressProvider
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.Captor
import org.mockito.Mock
-import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.whenever
/**
* Translates items away/towards the hinge when the device is opened/closed. This is controlled by
@@ -47,11 +46,16 @@
@RunWith(AndroidTestingRunner::class)
class KeyguardUnfoldTransitionTest : SysuiTestCase() {
- @Mock private lateinit var progressProvider: NaturalRotationUnfoldProgressProvider
+ private val kosmos = Kosmos()
- @Captor private lateinit var progressListenerCaptor: ArgumentCaptor<TransitionProgressListener>
+ private val progressProvider: FakeUnfoldTransitionProvider =
+ kosmos.fakeUnfoldTransitionProgressProvider
- @Mock private lateinit var parent: ViewGroup
+ @Mock
+ private lateinit var keyguardRootView: KeyguardRootView
+
+ @Mock
+ private lateinit var notificationShadeWindowView: NotificationShadeWindowView
@Mock private lateinit var statusBarStateController: StatusBarStateController
@@ -66,13 +70,15 @@
xTranslationMax =
context.resources.getDimensionPixelSize(R.dimen.keyguard_unfold_translation_x).toFloat()
- underTest = KeyguardUnfoldTransition(context, statusBarStateController, progressProvider)
+ underTest = KeyguardUnfoldTransition(
+ context, keyguardRootView, notificationShadeWindowView,
+ statusBarStateController, progressProvider
+ )
- underTest.setup(parent)
+ underTest.setup()
underTest.statusViewCentered = false
- verify(progressProvider).addCallback(capture(progressListenerCaptor))
- progressListener = progressListenerCaptor.value
+ progressListener = progressProvider
}
@Test
@@ -81,7 +87,9 @@
underTest.statusViewCentered = true
val view = View(context)
- whenever(parent.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn(view)
+ whenever(keyguardRootView.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn(
+ view
+ )
progressListener.onTransitionStarted()
assertThat(view.translationX).isZero()
@@ -101,7 +109,9 @@
whenever(statusBarStateController.getState()).thenReturn(SHADE)
val view = View(context)
- whenever(parent.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn(view)
+ whenever(keyguardRootView.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn(
+ view
+ )
progressListener.onTransitionStarted()
assertThat(view.translationX).isZero()
@@ -121,7 +131,10 @@
whenever(statusBarStateController.getState()).thenReturn(KEYGUARD)
val view = View(context)
- whenever(parent.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn(view)
+ whenever(
+ notificationShadeWindowView
+ .findViewById<View>(R.id.lockscreen_clock_view_large)
+ ).thenReturn(view)
progressListener.onTransitionStarted()
assertThat(view.translationX).isZero()
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt
index 25a87b8..9580139 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt
@@ -22,10 +22,6 @@
import com.android.keyguard.LockIconView.ICON_LOCK
import com.android.systemui.doze.util.getBurnInOffset
import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED
-import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
-import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
-import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
-import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.util.mockito.whenever
import kotlinx.coroutines.Dispatchers
@@ -104,7 +100,7 @@
// WHEN dozing updates
mUnderTest.mIsDozingCallback.accept(true)
- mUnderTest.mDozeTransitionCallback.accept(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
+ mUnderTest.mDozeTransitionCallback.accept(1f)
// THEN the view's translation is updated to use the AoD burn-in offsets
verify(mLockIconView).setTranslationY(burnInOffset.toFloat())
@@ -113,7 +109,7 @@
// WHEN the device is no longer dozing
mUnderTest.mIsDozingCallback.accept(false)
- mUnderTest.mDozeTransitionCallback.accept(TransitionStep(AOD, LOCKSCREEN, 0f, FINISHED))
+ mUnderTest.mDozeTransitionCallback.accept(0f)
// THEN the view is updated to NO translation (no burn-in offsets anymore)
verify(mLockIconView).setTranslationY(0f)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityLoggerTest.kt
index deacac3..1ce21e77 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityLoggerTest.kt
@@ -16,7 +16,7 @@
package com.android.systemui.accessibility
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase
@@ -34,7 +34,7 @@
import org.mockito.junit.MockitoJUnit
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class AccessibilityLoggerTest : SysuiTestCase() {
@JvmField @Rule val mockito = MockitoJUnit.rule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java
index 9cb4fb3..cb8cfc2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java
@@ -20,10 +20,10 @@
import static org.junit.Assert.assertNotNull;
import android.hardware.display.DisplayManager;
-import android.testing.AndroidTestingRunner;
import android.view.Display;
import androidx.annotation.NonNull;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -33,7 +33,7 @@
import org.junit.runner.RunWith;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class DisplayIdIndexSupplierTest extends SysuiTestCase {
private DisplayIdIndexSupplier<Object> mDisplayIdIndexSupplier;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java
index 5bc9aa4..cbd535b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java
@@ -21,8 +21,10 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
-import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -34,8 +36,12 @@
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.Rect;
+import android.os.RemoteException;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.Display;
+import android.view.IRotationWatcher;
+import android.view.IWindowManager;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.View;
@@ -55,6 +61,8 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -73,9 +81,12 @@
private ValueAnimator mShowHideBorderAnimator;
private SurfaceControl.Transaction mTransaction;
private TestableWindowManager mWindowManager;
+ @Mock
+ private IWindowManager mIWindowManager;
@Before
public void setUp() {
+ MockitoAnnotations.initMocks(this);
getInstrumentation().runOnMainSync(() -> mSurfaceControlViewHost =
spy(new SurfaceControlViewHost(mContext, mContext.getDisplay(),
new InputTransferToken(), "FullscreenMagnification")));
@@ -88,9 +99,11 @@
mShowHideBorderAnimator = spy(newNullTargetObjectAnimator());
mFullscreenMagnificationController = new FullscreenMagnificationController(
mContext,
+ mContext.getMainThreadHandler(),
mContext.getMainExecutor(),
mContext.getSystemService(AccessibilityManager.class),
mContext.getSystemService(WindowManager.class),
+ mIWindowManager,
scvhSupplier,
mTransaction,
mShowHideBorderAnimator);
@@ -104,7 +117,8 @@
}
@Test
- public void enableFullscreenMagnification_visibleBorder() throws InterruptedException {
+ public void enableFullscreenMagnification_visibleBorder()
+ throws InterruptedException, RemoteException {
CountDownLatch transactionCommittedLatch = new CountDownLatch(1);
CountDownLatch animationEndLatch = new CountDownLatch(1);
mTransaction.addTransactionCommittedListener(
@@ -119,17 +133,21 @@
//Enable fullscreen magnification
mFullscreenMagnificationController
.onFullscreenMagnificationActivationChanged(true));
- assertTrue("Failed to wait for transaction committed",
- transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS));
- assertTrue("Failed to wait for animation to be finished",
- animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertWithMessage("Failed to wait for transaction committed")
+ .that(transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS))
+ .isTrue();
+ assertWithMessage("Failed to wait for animation to be finished")
+ .that(animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS))
+ .isTrue();
verify(mShowHideBorderAnimator).start();
+ verify(mIWindowManager)
+ .watchRotation(any(IRotationWatcher.class), eq(Display.DEFAULT_DISPLAY));
assertThat(mSurfaceControlViewHost.getView().isVisibleToUser()).isTrue();
}
@Test
public void disableFullscreenMagnification_reverseAnimationAndReleaseScvh()
- throws InterruptedException {
+ throws InterruptedException, RemoteException {
CountDownLatch transactionCommittedLatch = new CountDownLatch(1);
CountDownLatch enableAnimationEndLatch = new CountDownLatch(1);
CountDownLatch disableAnimationEndLatch = new CountDownLatch(1);
@@ -149,11 +167,12 @@
//Enable fullscreen magnification
mFullscreenMagnificationController
.onFullscreenMagnificationActivationChanged(true));
- assertTrue("Failed to wait for transaction committed",
- transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS));
- assertTrue("Failed to wait for enabling animation to be finished",
- enableAnimationEndLatch.await(
- ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertWithMessage("Failed to wait for transaction committed")
+ .that(transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS))
+ .isTrue();
+ assertWithMessage("Failed to wait for enabling animation to be finished")
+ .that(enableAnimationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS))
+ .isTrue();
verify(mShowHideBorderAnimator).start();
getInstrumentation().runOnMainSync(() ->
@@ -161,11 +180,12 @@
mFullscreenMagnificationController
.onFullscreenMagnificationActivationChanged(false));
- assertTrue("Failed to wait for disabling animation to be finished",
- disableAnimationEndLatch.await(
- ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertWithMessage("Failed to wait for disabling animation to be finished")
+ .that(disableAnimationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS))
+ .isTrue();
verify(mShowHideBorderAnimator).reverse();
verify(mSurfaceControlViewHost).release();
+ verify(mIWindowManager).removeRotationWatcher(any(IRotationWatcher.class));
}
@Test
@@ -188,10 +208,12 @@
() -> mFullscreenMagnificationController
.onFullscreenMagnificationActivationChanged(true));
- assertTrue("Failed to wait for transaction committed",
- transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS));
- assertTrue("Failed to wait for animation to be finished",
- animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertWithMessage("Failed to wait for transaction committed")
+ .that(transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS))
+ .isTrue();
+ assertWithMessage("Failed to wait for animation to be finished")
+ .that(animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS))
+ .isTrue();
verify(mShowHideBorderAnimator).reverse();
}
@@ -212,10 +234,12 @@
//Enable fullscreen magnification
mFullscreenMagnificationController
.onFullscreenMagnificationActivationChanged(true));
- assertTrue("Failed to wait for transaction committed",
- transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS));
- assertTrue("Failed to wait for animation to be finished",
- animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertWithMessage("Failed to wait for transaction committed")
+ .that(transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS))
+ .isTrue();
+ assertWithMessage("Failed to wait for animation to be finished")
+ .that(animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS))
+ .isTrue();
final Rect testWindowBounds = new Rect(
mWindowManager.getCurrentWindowMetrics().getBounds());
testWindowBounds.set(testWindowBounds.left, testWindowBounds.top,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
index 3164f8e..361a945 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
@@ -37,14 +37,15 @@
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.Display;
+import android.view.IWindowManager;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IMagnificationConnection;
import android.view.accessibility.IMagnificationConnectionCallback;
import android.view.accessibility.IRemoteMagnificationAnimationCallback;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.Flags;
@@ -67,7 +68,7 @@
* {@link Magnification}
*/
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class IMagnificationConnectionTest extends SysuiTestCase {
@@ -99,6 +100,8 @@
private SecureSettings mSecureSettings;
@Mock
private AccessibilityLogger mA11yLogger;
+ @Mock
+ private IWindowManager mIWindowManager;
private IMagnificationConnection mIMagnificationConnection;
private Magnification mMagnification;
@@ -117,9 +120,10 @@
mTestableLooper = TestableLooper.get(this);
assertNotNull(mTestableLooper);
mMagnification = new Magnification(getContext(),
- mTestableLooper.getLooper(), getContext().getMainExecutor(), mCommandQueue,
+ mTestableLooper.getLooper(), mContext.getMainExecutor(), mCommandQueue,
mModeSwitchesController, mSysUiState, mOverviewProxyService, mSecureSettings,
- mDisplayTracker, getContext().getSystemService(DisplayManager.class), mA11yLogger);
+ mDisplayTracker, getContext().getSystemService(DisplayManager.class),
+ mA11yLogger, mIWindowManager);
mMagnification.mWindowMagnificationControllerSupplier =
new FakeWindowMagnificationControllerSupplier(
mContext.getSystemService(DisplayManager.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationGestureDetectorTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationGestureDetectorTest.java
index ad02179..7b06dd6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationGestureDetectorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationGestureDetectorTest.java
@@ -26,11 +26,11 @@
import android.os.Handler;
import android.os.SystemClock;
-import android.testing.AndroidTestingRunner;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -45,7 +45,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class MagnificationGestureDetectorTest extends SysuiTestCase {
private static final float ACTION_DOWN_X = 100;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
index 1a88545..5be1180 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
@@ -58,7 +58,6 @@
import android.graphics.Rect;
import android.os.Handler;
import android.os.SystemClock;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.Choreographer;
import android.view.MotionEvent;
@@ -71,6 +70,7 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ImageView;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
@@ -90,7 +90,7 @@
import java.util.List;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class MagnificationModeSwitchTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java
index 9eead6a..d0f8e78 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java
@@ -22,9 +22,9 @@
import static org.mockito.Mockito.verify;
import android.content.pm.ActivityInfo;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
@@ -40,7 +40,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
/** Tests the MagnificationSettingsController. */
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class MagnificationSettingsControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
index bbdd805..17b7e21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
@@ -40,13 +40,14 @@
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.os.RemoteException;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.Display;
+import android.view.IWindowManager;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IMagnificationConnection;
import android.view.accessibility.IMagnificationConnectionCallback;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -64,7 +65,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class MagnificationTest extends SysuiTestCase {
@@ -93,6 +94,8 @@
private MagnificationSettingsController mMagnificationSettingsController;
@Mock
private AccessibilityLogger mA11yLogger;
+ @Mock
+ private IWindowManager mIWindowManager;
@Before
public void setUp() throws Exception {
@@ -122,10 +125,10 @@
mCommandQueue = new CommandQueue(getContext(), mDisplayTracker);
mMagnification = new Magnification(getContext(),
- getContext().getMainThreadHandler(), getContext().getMainExecutor(),
+ getContext().getMainThreadHandler(), mContext.getMainExecutor(),
mCommandQueue, mModeSwitchesController,
mSysUiState, mOverviewProxyService, mSecureSettings, mDisplayTracker,
- getContext().getSystemService(DisplayManager.class), mA11yLogger);
+ getContext().getSystemService(DisplayManager.class), mA11yLogger, mIWindowManager);
mMagnification.mWindowMagnificationControllerSupplier = new FakeControllerSupplier(
mContext.getSystemService(DisplayManager.class), mWindowMagnificationController);
mMagnification.mMagnificationSettingsSupplier = new FakeSettingsSupplier(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java
index e81613e..8f9b7c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java
@@ -27,12 +27,12 @@
import android.content.Context;
import android.graphics.Point;
-import android.testing.AndroidTestingRunner;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -45,7 +45,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class MirrorWindowControlTest extends SysuiTestCase {
@Mock WindowManager mWindowManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
index 3c97423..6e94297 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
@@ -21,11 +21,11 @@
import android.content.pm.ActivityInfo;
import android.hardware.display.DisplayManager;
import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.Display;
import android.view.View;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -39,7 +39,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
/** Tests the ModeSwitchesController. */
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class ModeSwitchesControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/SecureSettingsContentObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SecureSettingsContentObserverTest.java
index 9c601a8..9222fc2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/SecureSettingsContentObserverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SecureSettingsContentObserverTest.java
@@ -21,8 +21,8 @@
import android.app.ActivityManager;
import android.content.Context;
import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -34,7 +34,7 @@
import org.mockito.Mockito;
/** Test for {@link SecureSettingsContentObserver}. */
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class SecureSettingsContentObserverTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java
index c674294..f46b2f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java
@@ -28,10 +28,10 @@
import android.os.RemoteException;
import android.telecom.TelecomManager;
import android.telephony.TelephonyManager;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.KeyEvent;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -55,7 +55,7 @@
@TestableLooper.RunWithLooper
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class SystemActionsTest extends SysuiTestCase {
@Mock
private UserTracker mUserTracker;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index cb42078..f57003e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -72,7 +72,6 @@
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableResources;
import android.text.TextUtils;
@@ -90,6 +89,7 @@
import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
@@ -125,7 +125,7 @@
@LargeTest
@TestableLooper.RunWithLooper
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@RequiresFlagsDisabled(Flags.FLAG_CREATE_WINDOWLESS_WINDOW_MAGNIFIER)
public class WindowMagnificationControllerTest extends SysuiTestCase {
@@ -1511,4 +1511,4 @@
});
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java
index 01e4d58..e272682 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java
@@ -71,7 +71,6 @@
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableResources;
import android.text.TextUtils;
@@ -94,6 +93,7 @@
import android.window.InputTransferToken;
import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import com.android.systemui.Flags;
@@ -129,7 +129,7 @@
@LargeTest
@TestableLooper.RunWithLooper
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@RequiresFlagsEnabled(Flags.FLAG_CREATE_WINDOWLESS_WINDOW_MAGNIFIER)
public class WindowMagnificationControllerWindowlessMagnifierTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java
index 93c0eea..ad9053a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java
@@ -23,10 +23,10 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.Size;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -37,7 +37,7 @@
import org.junit.runner.RunWith;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class WindowMagnificationFrameSizePrefsTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
index 138fed2..003f7e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
@@ -47,7 +47,6 @@
import android.graphics.Rect;
import android.os.UserHandle;
import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.View;
import android.view.ViewGroup;
@@ -59,6 +58,7 @@
import android.widget.LinearLayout;
import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
@@ -77,7 +77,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class WindowMagnificationSettingsTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryTest.kt
index aff52f5..c4a92bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryTest.kt
@@ -18,8 +18,8 @@
package com.android.systemui.accessibility.data.repository
-import android.testing.AndroidTestingRunner
import android.view.accessibility.AccessibilityManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -37,7 +37,7 @@
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class AccessibilityRepositoryTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
index 095c945..b71739a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
@@ -30,11 +30,11 @@
import android.hardware.display.DisplayManager;
import android.os.UserHandle;
import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardUpdateMonitor;
@@ -59,7 +59,7 @@
import org.mockito.junit.MockitoRule;
/** Test for {@link AccessibilityFloatingMenuController}. */
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java
index 630db62..b08f97a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java
@@ -21,10 +21,10 @@
import static org.mockito.Mockito.when;
import android.graphics.drawable.Drawable;
-import android.testing.AndroidTestingRunner;
import android.view.LayoutInflater;
import android.view.View;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.accessibility.dialog.AccessibilityTarget;
@@ -43,7 +43,7 @@
/** Tests for {@link AccessibilityTargetAdapter}. */
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class AccessibilityTargetAdapterTest extends SysuiTestCase {
@Mock
private AccessibilityTarget mAccessibilityTarget;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java
index 4b87588..5b2afe7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java
@@ -20,10 +20,10 @@
import static org.mockito.Mockito.mock;
-import android.testing.AndroidTestingRunner;
import android.text.SpannableStringBuilder;
import android.view.View;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -37,7 +37,7 @@
/** Tests for {@link AnnotationLinkSpan}. */
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class AnnotationLinkSpanTest extends SysuiTestCase {
private AnnotationLinkSpan.LinkInfo mLinkInfo;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
index abc95bc..19b2700 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
@@ -22,11 +22,11 @@
import android.annotation.NonNull;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.Flags;
@@ -46,7 +46,7 @@
/** Tests for {@link DragToInteractAnimationController}. */
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class DragToInteractAnimationControllerTest extends SysuiTestCase {
private DragToInteractAnimationController mDragToInteractAnimationController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java
index 34a2e87..b597737 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java
@@ -19,11 +19,11 @@
import static com.google.common.truth.Truth.assertThat;
import android.content.res.Resources;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.WindowManager;
import android.widget.TextView;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -35,7 +35,7 @@
/** Tests for {@link MenuEduTooltipView}. */
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class MenuEduTooltipViewTest extends SysuiTestCase {
private MenuViewAppearance mMenuViewAppearance;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
index 1faa8ac..24f3a29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
@@ -25,9 +25,9 @@
import android.content.Context;
import android.content.res.Configuration;
-import android.testing.AndroidTestingRunner;
import android.view.accessibility.AccessibilityManager;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -47,7 +47,7 @@
import java.util.Locale;
/** Tests for {@link MenuInfoRepository}. */
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class MenuInfoRepositoryTest extends SysuiTestCase {
@Rule
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
index 1f7d033..c5509ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
@@ -30,7 +30,6 @@
import android.graphics.Rect;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
@@ -39,6 +38,7 @@
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.Flags;
@@ -60,7 +60,7 @@
/** Tests for {@link MenuItemAccessibilityDelegate}. */
@SmallTest
@TestableLooper.RunWithLooper
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class MenuItemAccessibilityDelegateTest extends SysuiTestCase {
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
index 9e8c6b3..4373c88 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
@@ -30,7 +30,6 @@
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.MotionEvent;
import android.view.WindowManager;
@@ -38,6 +37,7 @@
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.recyclerview.widget.RecyclerView;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.accessibility.dialog.AccessibilityTarget;
@@ -62,7 +62,7 @@
import java.util.List;
/** Tests for {@link MenuListViewTouchHandler}. */
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
public class MenuListViewTouchHandlerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactoryTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactoryTest.java
index 9dd337e..2746fef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactoryTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactoryTest.java
@@ -19,8 +19,8 @@
import static com.google.common.truth.Truth.assertThat;
import android.app.Notification;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -29,7 +29,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class MenuNotificationFactoryTest extends SysuiTestCase {
private MenuNotificationFactory mMenuNotificationFactory;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
index 31824ec..bd1a7f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
@@ -27,7 +27,6 @@
import android.content.Context;
import android.graphics.Insets;
import android.graphics.Rect;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.View;
import android.view.ViewGroup;
@@ -36,6 +35,7 @@
import android.view.WindowMetrics;
import android.view.accessibility.AccessibilityManager;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -50,7 +50,7 @@
import org.mockito.junit.MockitoRule;
/** Tests for {@link MenuViewLayerController}. */
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
public class MenuViewLayerControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
index 05d7560..38095c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
@@ -61,7 +61,6 @@
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.ArraySet;
import android.view.View;
@@ -72,6 +71,7 @@
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.SpringAnimation;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.accessibility.common.ShortcutConstants;
@@ -101,7 +101,7 @@
import java.util.List;
/** Tests for {@link MenuViewLayer}. */
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
public class MenuViewLayerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
index f6288b6..103449b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
@@ -29,11 +29,11 @@
import android.graphics.Rect;
import android.graphics.drawable.GradientDrawable;
import android.platform.test.annotations.EnableFlags;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.Flags;
@@ -53,7 +53,7 @@
import org.mockito.junit.MockitoRule;
/** Tests for {@link MenuView}. */
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
public class MenuViewTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/PositionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/PositionTest.java
index 05f306b..8fb71fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/PositionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/PositionTest.java
@@ -18,8 +18,8 @@
import static com.google.common.truth.Truth.assertThat;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -29,7 +29,7 @@
/** Tests for {@link Position}. */
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class PositionTest extends SysuiTestCase {
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimatorTest.java
index d77a80a..f67e8d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimatorTest.java
@@ -20,8 +20,8 @@
import android.os.Handler;
import android.os.Looper;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -35,7 +35,7 @@
/** Tests for {@link RadiiAnimator}. */
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class RadiiAnimatorTest extends SysuiTestCase {
float[] mResultRadii = new float[RadiiAnimator.RADII_COUNT];
final AtomicBoolean mAnimationStarted = new AtomicBoolean(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt
index e371b39..0bd00fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt
@@ -18,12 +18,12 @@
import android.content.res.Configuration
import android.os.Handler
import android.provider.Settings
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.Button
import android.widget.SeekBar
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogTransitionAnimator
@@ -58,7 +58,7 @@
/** Tests for [FontScalingDialogDelegate]. */
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class FontScalingDialogDelegateTest : SysuiTestCase() {
private lateinit var fontScalingDialogDelegate: FontScalingDialogDelegate
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
index 0db0de2..8f7dc7cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
@@ -39,11 +39,11 @@
import android.os.Handler;
import android.platform.test.annotations.EnableFlags;
import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.View;
import android.widget.LinearLayout;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.settingslib.bluetooth.BluetoothEventManager;
@@ -78,7 +78,7 @@
import java.util.List;
/** Tests for {@link HearingDevicesDialogDelegate}. */
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManagerTest.java
index cb9c26c..09aa2868 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManagerTest.java
@@ -21,9 +21,10 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.testing.AndroidTestingRunner;
+import android.bluetooth.BluetoothDevice;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -42,7 +43,7 @@
import org.mockito.junit.MockitoRule;
/** Tests for {@link HearingDevicesDialogManager}. */
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
public class HearingDevicesDialogManagerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapterTest.java
index d16db65..9359adf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapterTest.java
@@ -20,9 +20,9 @@
import static org.mockito.Mockito.when;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -41,7 +41,7 @@
import java.util.List;
/** Tests for {@link HearingDevicesListAdapter}. */
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
public class HearingDevicesListAdapterTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java
index 7172923..17ce1dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java
@@ -28,9 +28,9 @@
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -48,7 +48,7 @@
/**
* Tests for {@link HearingDevicesToolItemParser}.
*/
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
public class HearingDevicesToolItemParserTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/InputSessionTest.java b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/InputSessionTest.java
index 2f4999b..8fca557 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/InputSessionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/InputSessionTest.java
@@ -24,13 +24,13 @@
import static org.mockito.Mockito.when;
import android.platform.test.annotations.EnableFlags;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.Choreographer;
import android.view.GestureDetector;
import android.view.InputEvent;
import android.view.MotionEvent;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.Flags;
@@ -50,7 +50,7 @@
* A test suite for exercising {@link InputSession}.
*/
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper()
public class InputSessionTest extends SysuiTestCase {
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java
index 358e8cb..4118c90 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java
@@ -36,7 +36,6 @@
import android.hardware.display.DisplayManager;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.GestureDetector;
import android.view.IWindowManager;
@@ -50,6 +49,7 @@
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.Flags;
@@ -79,7 +79,7 @@
import java.util.stream.Stream;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class TouchMonitorTest extends SysuiTestCase {
private KosmosJavaAdapter mKosmos;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
index 70a544c..9aaf295 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
@@ -9,7 +9,6 @@
import android.graphics.Rect
import android.os.Looper
import android.platform.test.flag.junit.SetFlagsRule
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.IRemoteAnimationFinishedCallback
import android.view.RemoteAnimationAdapter
@@ -20,6 +19,7 @@
import android.widget.LinearLayout
import android.window.RemoteTransition
import android.window.TransitionFilter
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.shared.Flags
@@ -49,7 +49,7 @@
import org.mockito.junit.MockitoJUnit
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class ActivityTransitionAnimatorTest : SysuiTestCase() {
private val transitionContainer = LinearLayout(mContext)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt
index e3be3822..37f549a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt
@@ -16,9 +16,9 @@
package com.android.systemui.animation
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import androidx.core.animation.doOnEnd
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.FlakyTest
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -28,7 +28,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
@RunWithLooper
@FlakyTest(bugId = 302149604)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt
index e14762cd..a60fb76 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt
@@ -5,7 +5,6 @@
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.ViewUtils
import android.view.View
@@ -14,6 +13,7 @@
import android.view.WindowManager
import android.widget.FrameLayout
import android.widget.LinearLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.jank.Cuj
import com.android.internal.policy.DecorView
@@ -39,7 +39,7 @@
import org.mockito.junit.MockitoRule
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class DialogTransitionAnimatorTest : SysuiTestCase() {
private val kosmos = testKosmos()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt
index 5e1a8e1..ec42b7f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt
@@ -20,14 +20,14 @@
import android.graphics.fonts.Font
import android.graphics.fonts.FontVariationAxis
import android.graphics.text.TextRunShaper
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class FontInterpolatorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/FontVariationUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/FontVariationUtilsTest.kt
index 070cad7..b0f81c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/FontVariationUtilsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/FontVariationUtilsTest.kt
@@ -1,6 +1,6 @@
package com.android.systemui.animation
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import junit.framework.Assert
@@ -12,7 +12,7 @@
private const val TAG_OPSZ = "opsz"
private const val TAG_ROND = "ROND"
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class FontVariationUtilsTest : SysuiTestCase() {
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt
index 42fcd54..e492c63 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt
@@ -17,10 +17,10 @@
package com.android.systemui.animation
import android.os.HandlerThread
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
import android.widget.FrameLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.SysuiTestCase
@@ -31,7 +31,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class GhostedViewTransitionAnimatorControllerTest : SysuiTestCase() {
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt
index 263d375..6ba1715 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt
@@ -19,10 +19,10 @@
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.graphics.Typeface
-import android.testing.AndroidTestingRunner
import android.text.Layout
import android.text.StaticLayout
import android.text.TextPaint
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
@@ -38,7 +38,7 @@
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class TextAnimatorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt
index f6fcd16..cca5f35 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt
@@ -22,12 +22,12 @@
import android.graphics.Typeface
import android.graphics.fonts.Font
import android.graphics.fonts.FontFamily
-import android.testing.AndroidTestingRunner
import android.text.Layout
import android.text.StaticLayout
import android.text.TextDirectionHeuristic
import android.text.TextDirectionHeuristics
import android.text.TextPaint
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
@@ -62,7 +62,7 @@
typeface = Font.Builder(VF_FONT).setFontVariationSettings("'wght' 700").build().toTypeface()
}
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class TextInterpolatorTest : SysuiTestCase() {
lateinit var typefaceCache: TypefaceVariantCache
@@ -330,4 +330,4 @@
Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { draw(Canvas(it)) }!!
private fun TextInterpolator.toBitmap(width: Int, height: Int) =
- Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { draw(Canvas(it)) }
\ No newline at end of file
+ Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { draw(Canvas(it)) }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
index c2e6db3..a8c3af9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
@@ -1,12 +1,12 @@
package com.android.systemui.animation
import android.animation.ObjectAnimator
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.RelativeLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.app.animation.Interpolators
import com.android.systemui.SysuiTestCase
@@ -22,7 +22,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class
ViewHierarchyAnimatorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt
index 0ed84ea..4809d0e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt
@@ -2,6 +2,7 @@
import android.util.DisplayMetrics
import android.window.BackEvent
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.dpToPx
@@ -9,12 +10,11 @@
import junit.framework.TestCase.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
private data class BackInput(val progressX: Float, val progressY: Float, val edge: Int)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class BackAnimationSpecTest : SysuiTestCase() {
private var displayMetrics =
DisplayMetrics().apply {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackTransformationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackTransformationTest.kt
index 44a5467..d898d1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackTransformationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackTransformationTest.kt
@@ -1,6 +1,7 @@
package com.android.systemui.animation.back
import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.mock
@@ -8,13 +9,12 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class BackTransformationTest : SysuiTestCase() {
private val targetView: View = mock()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt
index 314abda..9548e29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt
@@ -2,6 +2,7 @@
import android.util.DisplayMetrics
import android.window.BackEvent
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.argumentCaptor
@@ -10,11 +11,10 @@
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.Mockito.verify
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class OnBackAnimationCallbackExtensionTest : SysuiTestCase() {
private val onBackProgress: (BackTransformation) -> Unit = mock()
private val onBackStart: (BackEvent) -> Unit = mock()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index 0d464cf..476d6e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -49,9 +49,9 @@
import android.media.AudioRecordingConfiguration;
import android.os.Looper;
import android.os.UserHandle;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.R;
@@ -75,7 +75,7 @@
import java.util.Map;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class AppOpsControllerTest extends SysuiTestCase {
private static final String TEST_PACKAGE_NAME = "test";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/assist/ui/DisplayUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/assist/ui/DisplayUtilsTest.java
index 4d582ab..828d367 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/assist/ui/DisplayUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/assist/ui/DisplayUtilsTest.java
@@ -21,8 +21,8 @@
import android.content.Context;
import android.content.res.Resources;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -35,7 +35,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class DisplayUtilsTest extends SysuiTestCase {
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt
index 3c073d5..2bd0976 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt
@@ -17,9 +17,9 @@
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.widget.ImageView
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.flags.Flags.FLAG_NEW_STATUS_BAR_ICONS
import com.android.systemui.res.R
@@ -33,7 +33,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class BatteryMeterViewTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
index 7c03d78..6dc4b10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -19,9 +19,9 @@
import android.graphics.Point
import android.hardware.biometrics.BiometricSourceType
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.util.DisplayMetrics
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.keyguard.KeyguardUpdateMonitor
@@ -67,7 +67,7 @@
@ExperimentalCoroutinesApi
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class AuthRippleControllerTest : SysuiTestCase() {
private lateinit var staticMockSession: MockitoSession
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationDialogFactoryTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationDialogFactoryTest.java
index d2c6957..197cb84 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationDialogFactoryTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationDialogFactoryTest.java
@@ -33,9 +33,9 @@
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -53,7 +53,7 @@
import java.util.concurrent.ExecutionException;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class BiometricNotificationDialogFactoryTest extends SysuiTestCase {
@Rule
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java
index a279d3e..20d9433 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java
@@ -36,9 +36,9 @@
import android.hardware.fingerprint.FingerprintManager;
import android.os.Handler;
import android.os.UserHandle;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardUpdateMonitor;
@@ -58,7 +58,7 @@
import java.util.Optional;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class BiometricNotificationServiceTest extends SysuiTestCase {
@Rule
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsUtilsTest.java
index 5b6aee6..d26ccbc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsUtilsTest.java
@@ -23,6 +23,7 @@
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.view.Surface;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -33,11 +34,10 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
-@RunWith(JUnit4.class)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class UdfpsUtilsTest extends SysuiTestCase {
@Rule
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt
index b3e845f..d215047 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt
@@ -29,6 +29,7 @@
import android.hardware.biometrics.events.AuthenticationAcquiredInfo
import android.hardware.biometrics.events.AuthenticationStartedInfo
import android.hardware.biometrics.events.AuthenticationStoppedInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.shared.model.AuthenticationReason
@@ -46,7 +47,6 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
@@ -54,7 +54,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class BiometricStatusRepositoryTest : SysuiTestCase() {
@JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
@Mock private lateinit var biometricManager: BiometricManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt
index eae953e..d9b7161 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt
@@ -21,6 +21,7 @@
import android.view.Display.DEFAULT_DISPLAY
import android.view.DisplayInfo
import android.view.Surface
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.DisplayStateRepository
@@ -42,12 +43,11 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.Mockito.spy
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class DisplayStateRepositoryTest : SysuiTestCase() {
private val display = mock<Display>()
private val testScope = TestScope(StandardTestDispatcher())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt
index f5e96c9..9c11405 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt
@@ -26,6 +26,7 @@
import android.hardware.face.FaceManager
import android.hardware.face.FaceSensorPropertiesInternal
import android.hardware.face.IFaceAuthenticatorsRegisteredCallback
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.shared.model.LockoutMode
@@ -46,7 +47,6 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
@@ -57,7 +57,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class FacePropertyRepositoryImplTest : SysuiTestCase() {
companion object {
private const val LOGICAL_CAMERA_ID_OUTER_FRONT = "0"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt
index 6391986..0209ab8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt
@@ -19,6 +19,7 @@
import android.database.ContentObserver
import android.os.Handler
import android.provider.Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -35,7 +36,6 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
@@ -47,7 +47,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class FaceSettingsRepositoryImplTest : SysuiTestCase() {
@JvmField @Rule var mockitoRule = MockitoJUnit.rule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt
index 7808c41..ff5a419f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt
@@ -23,6 +23,7 @@
import android.hardware.fingerprint.FingerprintSensorProperties
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
@@ -38,7 +39,6 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
@@ -47,7 +47,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class FingerprintRepositoryImplTest : SysuiTestCase() {
@JvmField @Rule var mockitoRule = MockitoJUnit.rule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt
index 2682633..22971bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.biometrics.data.repository
import android.hardware.biometrics.PromptInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
@@ -37,7 +38,6 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.verify
@@ -51,7 +51,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class PromptRepositoryImplTest : SysuiTestCase() {
@JvmField @Rule var mockitoRule = MockitoJUnit.rule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt
index 4cff3e6..5d2d20c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt
@@ -19,6 +19,7 @@
import android.app.ActivityManager
import android.content.ComponentName
import android.hardware.biometrics.BiometricFingerprintConstants
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.app.activityTaskManager
import com.android.systemui.SysuiTestCase
@@ -37,13 +38,12 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.Mockito
import org.mockito.Mockito.`when`
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class BiometricStatusInteractorImplTest : SysuiTestCase() {
private val kosmos = testKosmos()
private lateinit var underTest: BiometricStatusInteractorImpl
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt
index 8690d4e..4856f15 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt
@@ -4,6 +4,7 @@
import android.app.admin.DevicePolicyResourcesManager
import android.content.pm.UserInfo
import android.os.UserManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockscreenCredential
@@ -25,7 +26,6 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyLong
import org.mockito.Mock
@@ -38,7 +38,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class CredentialInteractorImplTest : SysuiTestCase() {
@JvmField @Rule var mockitoRule = MockitoJUnit.rule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt
index 31bdde2..f40b6b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt
@@ -1,5 +1,6 @@
package com.android.systemui.biometrics.domain.interactor
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
@@ -22,14 +23,13 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class DisplayStateInteractorImplTest : SysuiTestCase() {
@JvmField @Rule var mockitoRule = MockitoJUnit.rule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt
index 3f83ce3..a58efd3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt
@@ -19,6 +19,7 @@
import android.hardware.biometrics.AuthenticateOptions
import android.hardware.biometrics.IBiometricContextListener
import android.hardware.biometrics.IBiometricContextListener.FoldState
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -40,12 +41,11 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.junit.MockitoJUnit
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class LogContextInteractorImplTest : SysuiTestCase() {
@JvmField @Rule var mockitoRule = MockitoJUnit.rule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt
index c4d0d23..5a36376 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt
@@ -3,6 +3,7 @@
import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton
import android.hardware.biometrics.PromptInfo
import android.hardware.biometrics.PromptVerticalListContentView
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.FakePromptRepository
@@ -29,7 +30,6 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.junit.MockitoJUnit
private const val USER_ID = 22
@@ -39,7 +39,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class PromptCredentialInteractorTest : SysuiTestCase() {
@JvmField @Rule var mockitoRule = MockitoJUnit.rule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
index 6e78e33..720f207 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
@@ -22,6 +22,7 @@
import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton
import android.hardware.biometrics.PromptInfo
import android.hardware.biometrics.PromptVerticalListContentView
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.SysuiTestCase
@@ -47,13 +48,12 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.Mock
import org.mockito.junit.MockitoJUnit
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class PromptSelectorInteractorImplTest : SysuiTestCase() {
companion object {
private const val TITLE = "hey there"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
index 5e7adb7..3d63c5b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
@@ -19,6 +19,7 @@
import android.graphics.Rect
import android.view.MotionEvent
import android.view.Surface
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
@@ -34,7 +35,6 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Captor
@@ -44,7 +44,7 @@
import org.mockito.junit.MockitoJUnit
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class UdfpsOverlayInteractorTest : SysuiTestCase() {
@JvmField @Rule var mockitoRule = MockitoJUnit.rule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt
index d10b935..08f139c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt
@@ -4,8 +4,10 @@
import android.hardware.biometrics.PromptContentItemBulletedText
import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton
import android.hardware.biometrics.PromptVerticalListContentView
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.Utils.toBitmap
import com.android.systemui.biometrics.fingerprintSensorPropertiesInternal
import com.android.systemui.biometrics.promptInfo
import com.android.systemui.biometrics.shared.model.BiometricModalities
@@ -15,19 +17,19 @@
import com.google.common.util.concurrent.MoreExecutors
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
private const val USER_ID = 2
private const val OPERATION_ID = 8L
private const val OP_PACKAGE_NAME = "biometric.testapp"
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class BiometricPromptRequestTest : SysuiTestCase() {
@Test
fun biometricRequestFromPromptInfo() {
val logoRes = R.drawable.ic_cake
+ val logoBitmapFromRes = context.getDrawable(logoRes).toBitmap()
val logoDescription = "test cake"
val title = "what"
val subtitle = "a"
@@ -44,6 +46,7 @@
BiometricPromptRequest.Biometric(
promptInfo(
logoRes = logoRes,
+ logoBitmap = logoBitmapFromRes,
logoDescription = logoDescription,
title = title,
subtitle = subtitle,
@@ -56,7 +59,8 @@
OP_PACKAGE_NAME,
)
- assertThat(request.logoRes).isEqualTo(logoRes)
+ assertThat(request.logoBitmap).isNotNull()
+ assertThat(request.logoBitmap!!.sameAs(logoBitmapFromRes)).isTrue()
assertThat(request.logoDescription).isEqualTo(logoDescription)
assertThat(request.title).isEqualTo(title)
assertThat(request.subtitle).isEqualTo(subtitle)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt
index 74c4313..4d8fafc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.biometrics.shared.model
import android.hardware.fingerprint.FingerprintSensorProperties
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.faceSensorPropertiesInternal
@@ -24,10 +25,9 @@
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class BiometricModalitiesTest : SysuiTestCase() {
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt
index 95b72d5..f9bedc9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt
@@ -21,12 +21,13 @@
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.Test
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+import platform.test.runner.parameterized.Parameter
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-import org.junit.runners.Parameterized.Parameters
@SmallTest
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
class BoundingBoxOverlapDetectorTest(val testCase: TestCase) : SysuiTestCase() {
val underTest = BoundingBoxOverlapDetector(1f)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetectorTest.kt
index 317141b..33ddbf1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetectorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetectorTest.kt
@@ -22,12 +22,13 @@
import com.android.systemui.biometrics.EllipseOverlapDetectorParams
import com.google.common.truth.Truth.assertThat
import org.junit.Test
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+import platform.test.runner.parameterized.Parameter
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-import org.junit.runners.Parameterized.Parameters
@SmallTest
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
class EllipseOverlapDetectorTest(val testCase: TestCase) : SysuiTestCase() {
val underTest =
EllipseOverlapDetector(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt
index 3e5c43a..3863b3c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt
@@ -5,12 +5,13 @@
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.Test
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+import platform.test.runner.parameterized.Parameter
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-import org.junit.runners.Parameterized.Parameters
@SmallTest
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
class NormalizedTouchDataTest(val testCase: TestCase) : SysuiTestCase() {
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt
index aff93bd..a4653e7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt
@@ -27,12 +27,13 @@
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.google.common.truth.Truth.assertThat
import org.junit.Test
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+import platform.test.runner.parameterized.Parameter
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-import org.junit.runners.Parameterized.Parameters
@SmallTest
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
class SinglePointerTouchProcessorTest(val testCase: TestCase) : SysuiTestCase() {
private val overlapDetector = FakeOverlapDetector()
private val underTest = SinglePointerTouchProcessor(overlapDetector)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
index ec2b104..4238254 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
@@ -32,6 +32,7 @@
import android.view.WindowMetrics
import android.view.layoutInflater
import android.view.windowManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.airbnb.lottie.LottieAnimationView
import com.android.keyguard.keyguardUpdateMonitor
@@ -61,7 +62,6 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.any
@@ -76,7 +76,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class SideFpsOverlayViewBinderTest : SysuiTestCase() {
private val kosmos = testKosmos()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModelTest.kt
index 9e804c1..e4c5cd4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModelTest.kt
@@ -1,5 +1,6 @@
package com.android.systemui.biometrics.ui.viewmodel
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.FakePromptRepository
@@ -19,7 +20,6 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
private const val USER_ID = 9
private const val REQUEST_ID = 9L
@@ -27,7 +27,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class CredentialViewModelTest : SysuiTestCase() {
private val dispatcher = UnconfinedTestDispatcher()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt
index 1b6aaab..77ddd31 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.biometrics.ui.viewmodel
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
@@ -34,7 +35,6 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mockito.verify
@@ -42,7 +42,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class DeviceEntryUdfpsTouchOverlayViewModelTest : SysuiTestCase() {
private val kosmos =
testKosmos().apply {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt
index 278a43e..3eb2ff3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt
@@ -16,16 +16,16 @@
package com.android.systemui.biometrics.ui.viewmodel
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.shared.model.BiometricModality
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class PromptAuthStateTest : SysuiTestCase() {
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptHistoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptHistoryImplTest.kt
index f9b590f..81132d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptHistoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptHistoryImplTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.biometrics.ui.viewmodel
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.shared.model.BiometricModality
@@ -23,10 +24,9 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class PromptHistoryImplTest : SysuiTestCase() {
private lateinit var history: PromptHistoryImpl
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index 1167fce..cc7dec56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -26,6 +26,7 @@
import android.content.res.Configuration
import android.graphics.Bitmap
import android.graphics.Point
+import android.graphics.Rect
import android.graphics.drawable.BitmapDrawable
import android.hardware.biometrics.BiometricFingerprintConstants
import android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT
@@ -48,6 +49,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
import com.android.systemui.biometrics.UdfpsUtils
+import com.android.systemui.biometrics.Utils.toBitmap
import com.android.systemui.biometrics.data.repository.FakeBiometricStatusRepository
import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
@@ -89,11 +91,12 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.junit.MockitoJUnit
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
private const val USER_ID = 4
private const val REQUEST_ID = 4L
@@ -105,7 +108,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCase() {
@JvmField @Rule var mockitoRule = MockitoJUnit.rule()
@@ -127,11 +130,32 @@
private val defaultLogoIcon = context.getDrawable(R.drawable.ic_android)
private val defaultLogoIconWithOverrides = context.getDrawable(R.drawable.ic_add)
private val logoResFromApp = R.drawable.ic_cake
- private val logoFromApp = context.getDrawable(logoResFromApp)
+ private val logoDrawableFromAppRes = context.getDrawable(logoResFromApp)
private val logoBitmapFromApp = Bitmap.createBitmap(400, 400, Bitmap.Config.RGB_565)
private val defaultLogoDescription = "Test Android App"
private val logoDescriptionFromApp = "Test Cake App"
private val packageNameForLogoWithOverrides = "should.use.overridden.logo"
+ /** Prompt panel size padding */
+ private val smallHorizontalGuidelinePadding =
+ context.resources.getDimensionPixelSize(
+ R.dimen.biometric_prompt_land_small_horizontal_guideline_padding
+ )
+ private val udfpsHorizontalGuidelinePadding =
+ context.resources.getDimensionPixelSize(
+ R.dimen.biometric_prompt_two_pane_udfps_horizontal_guideline_padding
+ )
+ private val udfpsHorizontalShorterGuidelinePadding =
+ context.resources.getDimensionPixelSize(
+ R.dimen.biometric_prompt_two_pane_udfps_shorter_horizontal_guideline_padding
+ )
+ private val mediumTopGuidelinePadding =
+ context.resources.getDimensionPixelSize(
+ R.dimen.biometric_prompt_one_pane_medium_top_guideline_padding
+ )
+ private val mediumHorizontalGuidelinePadding =
+ context.resources.getDimensionPixelSize(
+ R.dimen.biometric_prompt_two_pane_medium_horizontal_guideline_padding
+ )
private lateinit var fingerprintRepository: FakeFingerprintPropertyRepository
private lateinit var promptRepository: FakePromptRepository
@@ -221,7 +245,7 @@
context.setMockPackageManager(packageManager)
val resources = context.getOrCreateTestableResources()
- resources.addOverride(logoResFromApp, logoFromApp)
+ resources.addOverride(logoResFromApp, logoDrawableFromAppRes)
resources.addOverride(
R.array.biometric_dialog_package_names_for_logo_with_overrides,
arrayOf(packageNameForLogoWithOverrides)
@@ -1249,7 +1273,7 @@
@Test
@EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun descriptionOverriddenByVerticalListContentView() =
- runGenericTest(contentView = promptContentView, description = "test description") {
+ runGenericTest(description = "test description", contentView = promptContentView) {
val contentView by collectLastValue(viewModel.contentView)
val description by collectLastValue(viewModel.description)
@@ -1261,8 +1285,8 @@
@EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun descriptionOverriddenByContentViewWithMoreOptionsButton() =
runGenericTest(
- contentView = promptContentViewWithMoreOptionsButton,
- description = "test description"
+ description = "test description",
+ contentView = promptContentViewWithMoreOptionsButton
) {
val contentView by collectLastValue(viewModel.contentView)
val description by collectLastValue(viewModel.description)
@@ -1322,8 +1346,9 @@
@EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun logo_resSetByApp() =
runGenericTest(logoRes = logoResFromApp) {
+ val expectedBitmap = context.getDrawable(logoResFromApp).toBitmap()
val logo by collectLastValue(viewModel.logo)
- assertThat(logo).isEqualTo(logoFromApp)
+ assertThat((logo as BitmapDrawable).bitmap.sameAs(expectedBitmap)).isTrue()
}
@Test
@@ -1366,6 +1391,142 @@
}
@Test
+ @EnableFlags(FLAG_CONSTRAINT_BP)
+ fun position_bottom_rotation0() = runGenericTest {
+ displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
+ val position by collectLastValue(viewModel.position)
+ assertThat(position).isEqualTo(PromptPosition.Bottom)
+ } // TODO(b/335278136): Add test for no sensor landscape
+
+ @Test
+ @EnableFlags(FLAG_CONSTRAINT_BP)
+ fun position_bottom_forceLarge() = runGenericTest {
+ displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+ viewModel.onSwitchToCredential()
+ val position by collectLastValue(viewModel.position)
+ assertThat(position).isEqualTo(PromptPosition.Bottom)
+ }
+
+ @Test
+ @EnableFlags(FLAG_CONSTRAINT_BP)
+ fun position_bottom_largeScreen() = runGenericTest {
+ displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+ displayStateRepository.setIsLargeScreen(true)
+ val position by collectLastValue(viewModel.position)
+ assertThat(position).isEqualTo(PromptPosition.Bottom)
+ }
+
+ @Test
+ @EnableFlags(FLAG_CONSTRAINT_BP)
+ fun position_right_rotation90() = runGenericTest {
+ displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
+ val position by collectLastValue(viewModel.position)
+ assertThat(position).isEqualTo(PromptPosition.Right)
+ }
+
+ @Test
+ @EnableFlags(FLAG_CONSTRAINT_BP)
+ fun position_left_rotation270() = runGenericTest {
+ displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+ val position by collectLastValue(viewModel.position)
+ assertThat(position).isEqualTo(PromptPosition.Left)
+ }
+
+ @Test
+ @EnableFlags(FLAG_CONSTRAINT_BP)
+ fun position_top_rotation180() = runGenericTest {
+ displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
+ val position by collectLastValue(viewModel.position)
+ if (testCase.modalities.hasUdfps) {
+ assertThat(position).isEqualTo(PromptPosition.Top)
+ } else {
+ assertThat(position).isEqualTo(PromptPosition.Bottom)
+ }
+ }
+
+ @Test
+ @EnableFlags(FLAG_CONSTRAINT_BP)
+ fun guideline_bottom() = runGenericTest {
+ displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
+ val guidelineBounds by collectLastValue(viewModel.guidelineBounds)
+ assertThat(guidelineBounds).isEqualTo(Rect(0, mediumTopGuidelinePadding, 0, 0))
+ } // TODO(b/335278136): Add test for no sensor landscape
+
+ @Test
+ @EnableFlags(FLAG_CONSTRAINT_BP)
+ fun guideline_right() = runGenericTest {
+ displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
+
+ val isSmall = testCase.shouldStartAsImplicitFlow
+ val guidelineBounds by collectLastValue(viewModel.guidelineBounds)
+
+ if (isSmall) {
+ assertThat(guidelineBounds).isEqualTo(Rect(-smallHorizontalGuidelinePadding, 0, 0, 0))
+ } else if (testCase.modalities.hasUdfps) {
+ assertThat(guidelineBounds).isEqualTo(Rect(udfpsHorizontalGuidelinePadding, 0, 0, 0))
+ } else {
+ assertThat(guidelineBounds).isEqualTo(Rect(-mediumHorizontalGuidelinePadding, 0, 0, 0))
+ }
+ }
+
+ @Test
+ @EnableFlags(FLAG_CONSTRAINT_BP)
+ fun guideline_right_onlyShortTitle() =
+ runGenericTest(subtitle = "") {
+ displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
+
+ val isSmall = testCase.shouldStartAsImplicitFlow
+ val guidelineBounds by collectLastValue(viewModel.guidelineBounds)
+
+ if (!isSmall && testCase.modalities.hasUdfps) {
+ assertThat(guidelineBounds)
+ .isEqualTo(Rect(-udfpsHorizontalShorterGuidelinePadding, 0, 0, 0))
+ }
+ }
+
+ @Test
+ @EnableFlags(FLAG_CONSTRAINT_BP)
+ fun guideline_left() = runGenericTest {
+ displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+
+ val isSmall = testCase.shouldStartAsImplicitFlow
+ val guidelineBounds by collectLastValue(viewModel.guidelineBounds)
+
+ if (isSmall) {
+ assertThat(guidelineBounds).isEqualTo(Rect(0, 0, -smallHorizontalGuidelinePadding, 0))
+ } else if (testCase.modalities.hasUdfps) {
+ assertThat(guidelineBounds).isEqualTo(Rect(0, 0, udfpsHorizontalGuidelinePadding, 0))
+ } else {
+ assertThat(guidelineBounds).isEqualTo(Rect(0, 0, -mediumHorizontalGuidelinePadding, 0))
+ }
+ }
+
+ @Test
+ @EnableFlags(FLAG_CONSTRAINT_BP)
+ fun guideline_left_onlyShortTitle() =
+ runGenericTest(subtitle = "") {
+ displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+
+ val isSmall = testCase.shouldStartAsImplicitFlow
+ val guidelineBounds by collectLastValue(viewModel.guidelineBounds)
+
+ if (!isSmall && testCase.modalities.hasUdfps) {
+ assertThat(guidelineBounds)
+ .isEqualTo(Rect(0, 0, -udfpsHorizontalShorterGuidelinePadding, 0))
+ }
+ }
+
+ @Test
+ @EnableFlags(FLAG_CONSTRAINT_BP)
+ fun guideline_top() = runGenericTest {
+ displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
+ val guidelineBounds by collectLastValue(viewModel.guidelineBounds)
+ if (testCase.modalities.hasUdfps) {
+ assertThat(guidelineBounds).isEqualTo(Rect(0, 0, 0, 0))
+ }
+ }
+
+ @Test
fun iconViewLoaded() = runGenericTest {
val isIconViewLoaded by collectLastValue(viewModel.isIconViewLoaded)
// TODO(b/328677869): Add test for noIcon logic.
@@ -1395,9 +1556,10 @@
private fun runGenericTest(
doNotStart: Boolean = false,
allowCredentialFallback: Boolean = false,
+ subtitle: String? = "s",
description: String? = null,
contentView: PromptContentView? = null,
- logoRes: Int = -1,
+ logoRes: Int = 0,
logoBitmap: Bitmap? = null,
logoDescription: String? = null,
packageName: String = OP_PACKAGE_NAME,
@@ -1433,10 +1595,11 @@
allowCredentialFallback = allowCredentialFallback,
fingerprint = testCase.fingerprint,
face = testCase.face,
+ subtitleFromApp = subtitle,
descriptionFromApp = description,
contentViewFromApp = contentView,
logoResFromApp = logoRes,
- logoBitmapFromApp = logoBitmap,
+ logoBitmapFromApp = if (logoRes != 0) logoDrawableFromAppRes.toBitmap() else logoBitmap,
logoDescriptionFromApp = logoDescription,
packageName = packageName,
)
@@ -1476,7 +1639,7 @@
companion object {
@JvmStatic
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
fun data(): Collection<TestCase> = singleModalityTestCases + coexTestCases
private val singleModalityTestCases =
@@ -1620,26 +1783,28 @@
face: FaceSensorPropertiesInternal? = null,
requireConfirmation: Boolean = false,
allowCredentialFallback: Boolean = false,
+ subtitleFromApp: String? = "s",
descriptionFromApp: String? = null,
contentViewFromApp: PromptContentView? = null,
- logoResFromApp: Int = -1,
+ logoResFromApp: Int = 0,
logoBitmapFromApp: Bitmap? = null,
logoDescriptionFromApp: String? = null,
packageName: String = OP_PACKAGE_NAME,
) {
val info =
PromptInfo().apply {
- logoRes = logoResFromApp
- logoBitmap = logoBitmapFromApp
logoDescription = logoDescriptionFromApp
title = "t"
- subtitle = "s"
+ subtitle = subtitleFromApp
description = descriptionFromApp
contentView = contentViewFromApp
authenticators = listOf(face, fingerprint).extractAuthenticatorTypes()
isDeviceCredentialAllowed = allowCredentialFallback
isConfirmationRequested = requireConfirmation
}
+ if (logoBitmapFromApp != null) {
+ info.setLogo(logoResFromApp, logoBitmapFromApp)
+ }
setPrompt(
info,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
index b065393..3b2cf61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
@@ -27,6 +27,7 @@
import android.view.WindowInsets
import android.view.WindowMetrics
import android.view.windowManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.airbnb.lottie.model.KeyPath
import com.android.keyguard.keyguardUpdateMonitor
@@ -62,7 +63,6 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.spy
@@ -71,7 +71,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class SideFpsOverlayViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
@JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogDelegateTest.java
index 49f2043..7d4ee25 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogDelegateTest.java
@@ -28,11 +28,11 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.widget.Button;
import android.widget.TextView;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.testing.UiEventLoggerFake;
@@ -58,7 +58,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class BroadcastDialogDelegateTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt
index 8a1a082..4d7c499 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt
@@ -16,8 +16,8 @@
package com.android.systemui.bluetooth.qsdialog
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.dx.mockito.inline.extended.StaticMockitoSession
@@ -44,7 +44,7 @@
@ExperimentalCoroutinesApi
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class AudioSharingInteractorTest : SysuiTestCase() {
private val testDispatcher = UnconfinedTestDispatcher()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnInteractorTest.kt
index 4949716..ac5ceb8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnInteractorTest.kt
@@ -17,7 +17,7 @@
package com.android.systemui.bluetooth.qsdialog
import android.bluetooth.BluetoothAdapter
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.systemui.SysuiTestCase
@@ -39,7 +39,7 @@
import org.mockito.junit.MockitoRule
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class BluetoothAutoOnInteractorTest : SysuiTestCase() {
@get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
private val testDispatcher = StandardTestDispatcher()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt
index 85e2a8d..b7b2be4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt
@@ -17,7 +17,7 @@
package com.android.systemui.bluetooth.qsdialog
import android.bluetooth.BluetoothAdapter
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.bluetooth.BluetoothEventManager
import com.android.settingslib.bluetooth.LocalBluetoothManager
@@ -38,7 +38,7 @@
import org.mockito.junit.MockitoRule
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class BluetoothAutoOnRepositoryTest : SysuiTestCase() {
@get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
private val testDispatcher = StandardTestDispatcher()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt
index 6fe7d86..993cac7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt
@@ -16,8 +16,8 @@
package com.android.systemui.bluetooth.qsdialog
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.bluetooth.LocalBluetoothAdapter
import com.android.settingslib.bluetooth.LocalBluetoothManager
@@ -38,7 +38,7 @@
import org.mockito.junit.MockitoRule
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class BluetoothStateInteractorTest : SysuiTestCase() {
@get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt
index 7215619..d01fac3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt
@@ -17,7 +17,6 @@
package com.android.systemui.bluetooth.qsdialog
import android.graphics.drawable.Drawable
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.LayoutInflater
import android.view.View
@@ -27,6 +26,7 @@
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.settingslib.bluetooth.CachedBluetoothDevice
@@ -57,7 +57,7 @@
import org.mockito.junit.MockitoRule
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class BluetoothTileDialogDelegateTest : SysuiTestCase() {
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogRepositoryTest.kt
index 4aa6209..1f3dcac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogRepositoryTest.kt
@@ -17,8 +17,8 @@
package com.android.systemui.bluetooth.qsdialog
import android.bluetooth.BluetoothAdapter
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager
@@ -35,7 +35,7 @@
import org.mockito.junit.MockitoRule
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class BluetoothTileDialogRepositoryTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt
index 11f74c0..9abb85d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt
@@ -18,11 +18,11 @@
import android.bluetooth.BluetoothAdapter
import android.platform.test.annotations.EnableFlags
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.settingslib.bluetooth.CachedBluetoothDevice
@@ -62,7 +62,7 @@
import org.mockito.junit.MockitoRule
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@EnableFlags(Flags.FLAG_BLUETOOTH_QS_TILE_DIALOG_AUTO_ON_TOGGLE)
class BluetoothTileDialogViewModelTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorImplTest.kt
deleted file mode 100644
index 762137b..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorImplTest.kt
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.bluetooth.qsdialog
-
-import android.bluetooth.BluetoothDevice
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import androidx.test.filters.SmallTest
-import com.android.settingslib.bluetooth.CachedBluetoothDevice
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.testDispatcher
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.statusbar.phone.SystemUIDialog
-import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.whenever
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.verify
-import org.mockito.junit.MockitoJUnit
-import org.mockito.junit.MockitoRule
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-@OptIn(ExperimentalCoroutinesApi::class)
-class DeviceItemActionInteractorImplTest : SysuiTestCase() {
- @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
- private val kosmos = testKosmos().apply { testDispatcher = UnconfinedTestDispatcher() }
- private lateinit var actionInteractorImpl: DeviceItemActionInteractor
-
- @Mock private lateinit var dialog: SystemUIDialog
- @Mock private lateinit var cachedDevice: CachedBluetoothDevice
- @Mock private lateinit var device: BluetoothDevice
- @Mock private lateinit var deviceItem: DeviceItem
-
- @Before
- fun setUp() {
- actionInteractorImpl = kosmos.deviceItemActionInteractor
- whenever(deviceItem.cachedBluetoothDevice).thenReturn(cachedDevice)
- whenever(cachedDevice.address).thenReturn("ADDRESS")
- whenever(cachedDevice.device).thenReturn(device)
- }
-
- @Test
- fun testOnClick_connectedMedia_setActive() {
- with(kosmos) {
- testScope.runTest {
- whenever(deviceItem.type)
- .thenReturn(DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE)
- actionInteractorImpl.onClick(deviceItem, dialog)
- verify(cachedDevice).setActive()
- verify(bluetoothTileDialogLogger)
- .logDeviceClick(
- cachedDevice.address,
- DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE
- )
- }
- }
- }
-
- @Test
- fun testOnClick_activeMedia_disconnect() {
- with(kosmos) {
- testScope.runTest {
- whenever(deviceItem.type).thenReturn(DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE)
- actionInteractorImpl.onClick(deviceItem, dialog)
- verify(cachedDevice).disconnect()
- verify(bluetoothTileDialogLogger)
- .logDeviceClick(
- cachedDevice.address,
- DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE
- )
- }
- }
- }
-
- @Test
- fun testOnClick_connectedOtherDevice_disconnect() {
- with(kosmos) {
- testScope.runTest {
- whenever(deviceItem.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
- actionInteractorImpl.onClick(deviceItem, dialog)
- verify(cachedDevice).disconnect()
- verify(bluetoothTileDialogLogger)
- .logDeviceClick(cachedDevice.address, DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
- }
- }
- }
-
- @Test
- fun testOnClick_saved_connect() {
- with(kosmos) {
- testScope.runTest {
- whenever(deviceItem.type).thenReturn(DeviceItemType.SAVED_BLUETOOTH_DEVICE)
- actionInteractorImpl.onClick(deviceItem, dialog)
- verify(cachedDevice).connect()
- verify(bluetoothTileDialogLogger)
- .logDeviceClick(cachedDevice.address, DeviceItemType.SAVED_BLUETOOTH_DEVICE)
- }
- }
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt
index e8e37bc..5ff4634 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt
@@ -13,19 +13,28 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package com.android.systemui.bluetooth.qsdialog
import com.android.internal.logging.uiEventLogger
+import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.plugins.activityStarter
import com.android.systemui.util.mockito.mock
val Kosmos.bluetoothTileDialogLogger: BluetoothTileDialogLogger by Kosmos.Fixture { mock {} }
+val Kosmos.localBluetoothManager: LocalBluetoothManager by Kosmos.Fixture { mock {} }
+
+val Kosmos.dialogTransitionAnimator: DialogTransitionAnimator by Kosmos.Fixture { mock {} }
+
val Kosmos.deviceItemActionInteractor: DeviceItemActionInteractor by
Kosmos.Fixture {
- DeviceItemActionInteractorImpl(
+ DeviceItemActionInteractor(
+ activityStarter,
+ dialogTransitionAnimator,
+ localBluetoothManager,
testDispatcher,
bluetoothTileDialogLogger,
uiEventLogger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt
new file mode 100644
index 0000000..8246506
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt
@@ -0,0 +1,459 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.bluetooth.qsdialog
+
+import android.bluetooth.BluetoothDevice
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
+import com.android.settingslib.bluetooth.BluetoothUtils
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.LeAudioProfile
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.testKosmos
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@OptIn(ExperimentalCoroutinesApi::class)
+class DeviceItemActionInteractorTest : SysuiTestCase() {
+ @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
+ private val kosmos = testKosmos().apply { testDispatcher = UnconfinedTestDispatcher() }
+ private lateinit var actionInteractorImpl: DeviceItemActionInteractor
+ private lateinit var mockitoSession: StaticMockitoSession
+ private lateinit var activeMediaDeviceItem: DeviceItem
+ private lateinit var notConnectedDeviceItem: DeviceItem
+ private lateinit var connectedMediaDeviceItem: DeviceItem
+ private lateinit var connectedOtherDeviceItem: DeviceItem
+ @Mock private lateinit var dialog: SystemUIDialog
+ @Mock private lateinit var profileManager: LocalBluetoothProfileManager
+ @Mock private lateinit var leAudioProfile: LeAudioProfile
+ @Mock private lateinit var assistantProfile: LocalBluetoothLeBroadcastAssistant
+ @Mock private lateinit var bluetoothDevice: BluetoothDevice
+ @Mock private lateinit var bluetoothDeviceGroupId2: BluetoothDevice
+ @Mock private lateinit var cachedBluetoothDevice: CachedBluetoothDevice
+
+ @Before
+ fun setUp() {
+ mockitoSession =
+ mockitoSession().initMocks(this).mockStatic(BluetoothUtils::class.java).startMocking()
+ activeMediaDeviceItem =
+ DeviceItem(
+ type = DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE,
+ cachedBluetoothDevice = cachedBluetoothDevice,
+ deviceName = DEVICE_NAME,
+ connectionSummary = DEVICE_CONNECTION_SUMMARY,
+ iconWithDescription = null,
+ background = null
+ )
+ notConnectedDeviceItem =
+ DeviceItem(
+ type = DeviceItemType.SAVED_BLUETOOTH_DEVICE,
+ cachedBluetoothDevice = cachedBluetoothDevice,
+ deviceName = DEVICE_NAME,
+ connectionSummary = DEVICE_CONNECTION_SUMMARY,
+ iconWithDescription = null,
+ background = null
+ )
+ connectedMediaDeviceItem =
+ DeviceItem(
+ type = DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE,
+ cachedBluetoothDevice = cachedBluetoothDevice,
+ deviceName = DEVICE_NAME,
+ connectionSummary = DEVICE_CONNECTION_SUMMARY,
+ iconWithDescription = null,
+ background = null
+ )
+ connectedOtherDeviceItem =
+ DeviceItem(
+ type = DeviceItemType.CONNECTED_BLUETOOTH_DEVICE,
+ cachedBluetoothDevice = cachedBluetoothDevice,
+ deviceName = DEVICE_NAME,
+ connectionSummary = DEVICE_CONNECTION_SUMMARY,
+ iconWithDescription = null,
+ background = null
+ )
+ actionInteractorImpl = kosmos.deviceItemActionInteractor
+ }
+
+ @After
+ fun tearDown() {
+ mockitoSession.finishMocking()
+ }
+
+ @Test
+ fun testOnClick_connectedMedia_setActive() {
+ with(kosmos) {
+ testScope.runTest {
+ whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+ whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false)
+ actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog)
+ verify(cachedBluetoothDevice).setActive()
+ verify(bluetoothTileDialogLogger)
+ .logDeviceClick(
+ cachedBluetoothDevice.address,
+ DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE
+ )
+ }
+ }
+ }
+
+ @Test
+ fun testOnClick_activeMedia_disconnect() {
+ with(kosmos) {
+ testScope.runTest {
+ whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+ whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false)
+ actionInteractorImpl.onClick(activeMediaDeviceItem, dialog)
+ verify(cachedBluetoothDevice).disconnect()
+ verify(bluetoothTileDialogLogger)
+ .logDeviceClick(
+ cachedBluetoothDevice.address,
+ DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE
+ )
+ }
+ }
+ }
+
+ @Test
+ fun testOnClick_connectedOtherDevice_disconnect() {
+ with(kosmos) {
+ testScope.runTest {
+ whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+ whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false)
+ actionInteractorImpl.onClick(connectedOtherDeviceItem, dialog)
+ verify(cachedBluetoothDevice).disconnect()
+ verify(bluetoothTileDialogLogger)
+ .logDeviceClick(
+ cachedBluetoothDevice.address,
+ DeviceItemType.CONNECTED_BLUETOOTH_DEVICE
+ )
+ }
+ }
+ }
+
+ @Test
+ fun testOnClick_saved_connect() {
+ with(kosmos) {
+ testScope.runTest {
+ whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+ whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false)
+ actionInteractorImpl.onClick(notConnectedDeviceItem, dialog)
+ verify(cachedBluetoothDevice).connect()
+ verify(bluetoothTileDialogLogger)
+ .logDeviceClick(
+ cachedBluetoothDevice.address,
+ DeviceItemType.SAVED_BLUETOOTH_DEVICE
+ )
+ }
+ }
+ }
+
+ @Test
+ fun testOnClick_audioSharingDisabled_shouldNotLaunchSettings() {
+ with(kosmos) {
+ testScope.runTest {
+ whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+ whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false)
+
+ actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog)
+ verify(activityStarter, Mockito.never())
+ .postStartActivityDismissingKeyguard(
+ ArgumentMatchers.any(),
+ ArgumentMatchers.anyInt(),
+ ArgumentMatchers.any()
+ )
+ }
+ }
+ }
+
+ @Test
+ fun testOnClick_inAudioSharing_clickedDeviceHasSource_shouldNotLaunchSettings() {
+ with(kosmos) {
+ testScope.runTest {
+ whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+ whenever(cachedBluetoothDevice.connectableProfiles)
+ .thenReturn(listOf(leAudioProfile))
+
+ whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
+ whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
+ whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
+ whenever(profileManager.leAudioBroadcastAssistantProfile)
+ .thenReturn(assistantProfile)
+
+ whenever(BluetoothUtils.isBroadcasting(ArgumentMatchers.any())).thenReturn(true)
+ whenever(
+ BluetoothUtils.hasConnectedBroadcastSource(
+ ArgumentMatchers.any(),
+ ArgumentMatchers.any()
+ )
+ )
+ .thenReturn(true)
+
+ actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog)
+ verify(activityStarter, Mockito.never())
+ .postStartActivityDismissingKeyguard(
+ ArgumentMatchers.any(),
+ ArgumentMatchers.anyInt(),
+ ArgumentMatchers.any()
+ )
+ }
+ }
+ }
+
+ @Test
+ fun testOnClick_inAudioSharing_clickedDeviceNoSource_shouldLaunchSettings() {
+ with(kosmos) {
+ testScope.runTest {
+ whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+ whenever(cachedBluetoothDevice.device).thenReturn(bluetoothDevice)
+ whenever(cachedBluetoothDevice.connectableProfiles)
+ .thenReturn(listOf(leAudioProfile))
+
+ whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
+ whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
+ whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
+ whenever(profileManager.leAudioBroadcastAssistantProfile)
+ .thenReturn(assistantProfile)
+
+ whenever(BluetoothUtils.isBroadcasting(ArgumentMatchers.any())).thenReturn(true)
+ whenever(
+ BluetoothUtils.hasConnectedBroadcastSource(
+ ArgumentMatchers.any(),
+ ArgumentMatchers.any()
+ )
+ )
+ .thenReturn(false)
+
+ actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog)
+ verify(activityStarter)
+ .postStartActivityDismissingKeyguard(
+ ArgumentMatchers.any(),
+ ArgumentMatchers.anyInt(),
+ ArgumentMatchers.any()
+ )
+ }
+ }
+ }
+
+ @Test
+ fun testOnClick_noConnectedLeDevice_shouldNotLaunchSettings() {
+ with(kosmos) {
+ testScope.runTest {
+ whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+
+ whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
+ whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
+ whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
+ whenever(profileManager.leAudioBroadcastAssistantProfile)
+ .thenReturn(assistantProfile)
+
+ actionInteractorImpl.onClick(notConnectedDeviceItem, dialog)
+ verify(activityStarter, Mockito.never())
+ .postStartActivityDismissingKeyguard(
+ ArgumentMatchers.any(),
+ ArgumentMatchers.anyInt(),
+ ArgumentMatchers.any()
+ )
+ }
+ }
+ }
+
+ @Test
+ fun testOnClick_hasOneConnectedLeDevice_clickedNonLe_shouldNotLaunchSettings() {
+ with(kosmos) {
+ testScope.runTest {
+ whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+
+ whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
+ whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
+ whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
+ whenever(profileManager.leAudioBroadcastAssistantProfile)
+ .thenReturn(assistantProfile)
+
+ whenever(
+ assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any())
+ )
+ .thenReturn(listOf(bluetoothDevice))
+
+ actionInteractorImpl.onClick(notConnectedDeviceItem, dialog)
+ verify(activityStarter, Mockito.never())
+ .postStartActivityDismissingKeyguard(
+ ArgumentMatchers.any(),
+ ArgumentMatchers.anyInt(),
+ ArgumentMatchers.any()
+ )
+ }
+ }
+ }
+
+ @Test
+ fun testOnClick_hasOneConnectedLeDevice_clickedLe_shouldLaunchSettings() {
+ with(kosmos) {
+ testScope.runTest {
+ whenever(cachedBluetoothDevice.device).thenReturn(bluetoothDevice)
+ whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+ whenever(cachedBluetoothDevice.profiles).thenReturn(listOf(leAudioProfile))
+ whenever(leAudioProfile.isEnabled(ArgumentMatchers.any())).thenReturn(true)
+
+ whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
+ whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
+ whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
+ whenever(profileManager.leAudioBroadcastAssistantProfile)
+ .thenReturn(assistantProfile)
+
+ whenever(
+ assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any())
+ )
+ .thenReturn(listOf(bluetoothDevice))
+
+ actionInteractorImpl.onClick(notConnectedDeviceItem, dialog)
+ verify(activityStarter)
+ .postStartActivityDismissingKeyguard(
+ ArgumentMatchers.any(),
+ ArgumentMatchers.anyInt(),
+ ArgumentMatchers.any()
+ )
+ }
+ }
+ }
+
+ @Test
+ fun testOnClick_hasOneConnectedLeDevice_clickedConnectedLe_shouldNotLaunchSettings() {
+ with(kosmos) {
+ testScope.runTest {
+ whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+
+ whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
+ whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
+ whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
+ whenever(profileManager.leAudioBroadcastAssistantProfile)
+ .thenReturn(assistantProfile)
+
+ whenever(
+ assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any())
+ )
+ .thenReturn(listOf(bluetoothDevice))
+
+ actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog)
+ verify(activityStarter, Mockito.never())
+ .postStartActivityDismissingKeyguard(
+ ArgumentMatchers.any(),
+ ArgumentMatchers.anyInt(),
+ ArgumentMatchers.any()
+ )
+ }
+ }
+ }
+
+ @Test
+ fun testOnClick_hasTwoConnectedLeDevice_clickedNotConnectedLe_shouldNotLaunchSettings() {
+ with(kosmos) {
+ testScope.runTest {
+ whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+
+ whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
+ whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
+ whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
+ whenever(profileManager.leAudioBroadcastAssistantProfile)
+ .thenReturn(assistantProfile)
+
+ whenever(
+ assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any())
+ )
+ .thenReturn(listOf(bluetoothDevice, bluetoothDeviceGroupId2))
+ whenever(leAudioProfile.getGroupId(ArgumentMatchers.any())).thenAnswer {
+ val device = it.arguments.first() as BluetoothDevice
+ if (device == bluetoothDevice) GROUP_ID_1 else GROUP_ID_2
+ }
+
+ actionInteractorImpl.onClick(notConnectedDeviceItem, dialog)
+ verify(activityStarter, Mockito.never())
+ .postStartActivityDismissingKeyguard(
+ ArgumentMatchers.any(),
+ ArgumentMatchers.anyInt(),
+ ArgumentMatchers.any()
+ )
+ }
+ }
+ }
+
+ @Test
+ fun testOnClick_hasTwoConnectedLeDevice_clickedConnectedLe_shouldLaunchSettings() {
+ with(kosmos) {
+ testScope.runTest {
+ whenever(cachedBluetoothDevice.device).thenReturn(bluetoothDevice)
+ whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+ whenever(cachedBluetoothDevice.profiles).thenReturn(listOf(leAudioProfile))
+ whenever(leAudioProfile.isEnabled(ArgumentMatchers.any())).thenReturn(true)
+
+ whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
+ whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
+ whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
+ whenever(profileManager.leAudioBroadcastAssistantProfile)
+ .thenReturn(assistantProfile)
+
+ whenever(
+ assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any())
+ )
+ .thenReturn(listOf(bluetoothDevice, bluetoothDeviceGroupId2))
+ whenever(leAudioProfile.getGroupId(ArgumentMatchers.any())).thenAnswer {
+ val device = it.arguments.first() as BluetoothDevice
+ if (device == bluetoothDevice) GROUP_ID_1 else GROUP_ID_2
+ }
+
+ actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog)
+ verify(activityStarter)
+ .postStartActivityDismissingKeyguard(
+ ArgumentMatchers.any(),
+ ArgumentMatchers.anyInt(),
+ ArgumentMatchers.any()
+ )
+ }
+ }
+ }
+
+ private companion object {
+ const val DEVICE_NAME = "device"
+ const val DEVICE_CONNECTION_SUMMARY = "active"
+ const val DEVICE_ADDRESS = "address"
+ const val GROUP_ID_1 = 1
+ const val GROUP_ID_2 = 2
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt
index 4bcd9a9..a27ccc6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt
@@ -22,8 +22,8 @@
import android.media.AudioManager
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.flags.Flags
@@ -39,7 +39,7 @@
import org.mockito.junit.MockitoRule
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class DeviceItemFactoryTest : SysuiTestCase() {
@get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt
index 2b4f950..7f7abaf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt
@@ -20,8 +20,8 @@
import android.bluetooth.BluetoothDevice
import android.content.Context
import android.media.AudioManager
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.bluetooth.LocalBluetoothManager
@@ -43,7 +43,7 @@
import org.mockito.junit.MockitoRule
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class DeviceItemInteractorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt
index ca95822..923687b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt
@@ -25,11 +25,13 @@
import com.google.common.truth.Truth.assertThat
import java.util.Locale
import org.junit.Test
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameter
+import platform.test.runner.parameterized.Parameters
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
@SmallTest
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
class BouncerSceneLayoutTest : SysuiTestCase() {
data object Phone :
@@ -79,7 +81,7 @@
companion object {
@JvmStatic
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
fun testCases() =
listOf(
Phone to
@@ -158,7 +160,7 @@
}
}
- @Parameterized.Parameter @JvmField var testCase: TestCase? = null
+ @Parameter @JvmField var testCase: TestCase? = null
@Test
fun calculateLayout() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastSenderTest.kt
index 8e81727..1e9f855 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastSenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastSenderTest.kt
@@ -20,7 +20,7 @@
import android.content.Intent
import android.os.Bundle
import android.os.UserHandle
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.concurrency.FakeExecutor
@@ -34,7 +34,7 @@
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class BroadcastSenderTest : SysuiTestCase() {
@@ -138,4 +138,4 @@
verification.invoke()
assertThat(wakeLock.isHeld).isFalse()
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/PendingRemovalStoreTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/PendingRemovalStoreTest.kt
index 43d2cb8..c693ecc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/PendingRemovalStoreTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/PendingRemovalStoreTest.kt
@@ -2,7 +2,7 @@
import android.content.BroadcastReceiver
import android.os.UserHandle
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
@@ -14,7 +14,7 @@
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class PendingRemovalStoreTest : SysuiTestCase() {
@@ -78,4 +78,4 @@
assertThat(store.isPendingRemoval(receiverOne, user)).isTrue()
assertThat(store.isPendingRemoval(receiverTwo, user)).isFalse()
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt
index 582f301..d878352 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt
@@ -21,8 +21,8 @@
import android.content.IntentFilter
import android.os.Handler
import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
@@ -43,7 +43,7 @@
import org.mockito.MockitoAnnotations
import java.util.concurrent.Executor
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
@SmallTest
class UserBroadcastDispatcherTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
index 669795b..bea0db6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
@@ -24,6 +24,7 @@
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.ActivityIntentHelper
import com.android.systemui.SysuiTestCase
@@ -41,7 +42,6 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.anyInt
@@ -50,7 +50,7 @@
import org.mockito.Mockito.`when` as whenever
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class CameraGestureHelperTest : SysuiTestCase() {
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraIntentsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraIntentsTest.kt
index 1e522fc..3494024 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraIntentsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraIntentsTest.kt
@@ -17,8 +17,8 @@
package com.android.systemui.camera
import android.content.Intent
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import android.testing.AndroidTestingRunner
import com.android.systemui.SysuiTestCase
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -26,7 +26,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class CameraIntentsTest : SysuiTestCase() {
companion object {
val VALID_SECURE_INTENT = Intent(CameraIntents.DEFAULT_SECURE_CAMERA_INTENT_ACTION)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
index 11756d5..034bab8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
@@ -17,11 +17,11 @@
package com.android.systemui.charging
import android.graphics.Rect
-import android.testing.AndroidTestingRunner
import android.view.Surface
import android.view.View
import android.view.WindowManager
import android.view.WindowMetrics
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.systemui.res.R
@@ -49,7 +49,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class WiredChargingRippleControllerTest : SysuiTestCase() {
private lateinit var controller: WiredChargingRippleController
@Mock private lateinit var commandRegistry: CommandRegistry
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
index 6afbde0..88bfcf0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
@@ -29,10 +29,10 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.testing.AndroidTestingRunner;
import android.view.MotionEvent;
import android.view.accessibility.AccessibilityManager;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
@@ -58,7 +58,7 @@
import java.util.Set;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class BrightLineClassifierTest extends SysuiTestCase {
private BrightLineFalsingManager mBrightLineFalsingManager;
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
index 6e00b70..ec8cc4d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
@@ -25,10 +25,10 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.testing.AndroidTestingRunner;
import android.view.MotionEvent;
import android.view.accessibility.AccessibilityManager;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
@@ -53,7 +53,7 @@
import java.util.Set;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class BrightLineFalsingManagerTest extends SysuiTestCase {
private BrightLineFalsingManager mBrightLineFalsingManager;
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/DiagonalClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DiagonalClassifierTest.java
index 14dcd58..8e1be41 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/DiagonalClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DiagonalClassifierTest.java
@@ -24,8 +24,8 @@
import static org.mockito.Mockito.when;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.util.DeviceConfigProxyFake;
@@ -38,7 +38,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class DiagonalClassifierTest extends ClassifierTest {
// Next variable is not actually five, but is very close. 5 degrees is currently the value
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java
index ab6d5b7..cbfecee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java
@@ -21,8 +21,8 @@
import static com.google.common.truth.Truth.assertThat;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.util.DeviceConfigProxyFake;
@@ -33,7 +33,7 @@
import org.junit.runner.RunWith;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class DistanceClassifierTest extends ClassifierTest {
private FalsingDataProvider mDataProvider;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java
index 2ceee6d..9289867 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java
@@ -22,9 +22,9 @@
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.Mockito.when;
-import android.testing.AndroidTestingRunner;
import android.view.MotionEvent;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import org.junit.After;
@@ -38,7 +38,7 @@
import java.util.List;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class DoubleTapClassifierTest extends ClassifierTest {
private static final int TOUCH_SLOP = 100;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingA11yDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingA11yDelegateTest.kt
index 2c904e7..8e4bec3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingA11yDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingA11yDelegateTest.kt
@@ -16,10 +16,10 @@
package com.android.systemui.classifier
-import android.testing.AndroidTestingRunner
import android.view.View
import android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK
import android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import org.junit.Before
@@ -31,7 +31,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class FalsingA11yDelegateTest : SysuiTestCase() {
@Mock lateinit var falsingCollector: FalsingCollector
@Mock lateinit var view: View
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
index 5361cef..5d0bfd7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
@@ -25,11 +25,11 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardUpdateMonitor;
@@ -66,7 +66,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class FalsingCollectorImplTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
index 057b0a1..49c6239 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
@@ -25,11 +25,11 @@
import static org.mockito.Mockito.when;
import android.hardware.devicestate.DeviceStateManager.FoldStateListener;
-import android.testing.AndroidTestingRunner;
import android.util.DisplayMetrics;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.classifier.FalsingDataProvider.GestureFinalizedListener;
@@ -46,7 +46,7 @@
import java.util.List;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class FalsingDataProviderTest extends ClassifierTest {
private FalsingDataProvider mDataProvider;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java
index 38355c7..8e19a1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java
@@ -18,8 +18,8 @@
import static com.google.common.truth.Truth.assertThat;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -33,7 +33,7 @@
import java.util.Collections;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class HistoryTrackerTest extends SysuiTestCase {
private FakeSystemClock mSystemClock = new FakeSystemClock();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/PointerCountClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/PointerCountClassifierTest.java
index b8ea062..352a25c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/PointerCountClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/PointerCountClassifierTest.java
@@ -23,9 +23,9 @@
import static org.mockito.ArgumentMatchers.anyDouble;
import static org.mockito.ArgumentMatchers.anyInt;
-import android.testing.AndroidTestingRunner;
import android.view.MotionEvent;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import org.junit.After;
@@ -34,7 +34,7 @@
import org.junit.runner.RunWith;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class PointerCountClassifierTest extends ClassifierTest {
private FalsingClassifier mClassifier;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java
index 1c3922a..f965a11 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java
@@ -24,9 +24,9 @@
import static org.mockito.Mockito.when;
-import android.testing.AndroidTestingRunner;
import android.view.MotionEvent;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.plugins.FalsingManager;
@@ -40,7 +40,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class ProximityClassifierTest extends ClassifierTest {
private static final long NS_PER_MS = 1000000;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java
index e3c800e..65e9088 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java
@@ -20,9 +20,9 @@
import static org.mockito.Mockito.when;
-import android.testing.AndroidTestingRunner;
import android.view.MotionEvent;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import org.junit.After;
@@ -36,7 +36,7 @@
import java.util.List;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class SingleTapClassifierTest extends ClassifierTest {
private static final int TOUCH_SLOP = 100;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/TimeLimitedInputEventBufferTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/TimeLimitedInputEventBufferTest.java
index ad7afa3..9a27f38 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/TimeLimitedInputEventBufferTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/TimeLimitedInputEventBufferTest.java
@@ -19,11 +19,11 @@
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
-import android.testing.AndroidTestingRunner;
import android.view.InputEvent;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -35,7 +35,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class TimeLimitedInputEventBufferTest extends SysuiTestCase {
private static final long MAX_AGE_MS = 100;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java
index 588edb7..80c44e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java
@@ -33,8 +33,8 @@
import static org.mockito.Mockito.when;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import org.junit.Before;
@@ -44,7 +44,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class TypeClassifierTest extends ClassifierTest {
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java
index ae2b8bb..1fe7268 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java
@@ -20,8 +20,8 @@
import static com.google.common.truth.Truth.assertThat;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.util.DeviceConfigProxyFake;
@@ -34,7 +34,7 @@
import java.util.Random;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class ZigZagClassifierTest extends ClassifierTest {
private FalsingClassifier mClassifier;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardModelTest.kt
index c0dada4..5d76e32 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardModelTest.kt
@@ -22,8 +22,8 @@
import android.graphics.Bitmap
import android.net.Uri
import android.os.PersistableBundle
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import androidx.test.runner.AndroidJUnit4
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.whenever
import java.io.IOException
diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/coroutine/CoroutineResultTest.kt b/packages/SystemUI/tests/src/com/android/systemui/common/coroutine/CoroutineResultTest.kt
index d552c9d..de07cda 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/common/coroutine/CoroutineResultTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/common/coroutine/CoroutineResultTest.kt
@@ -14,7 +14,7 @@
package com.android.systemui.common.coroutine
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
@@ -28,7 +28,7 @@
/** atest SystemUITests:CoroutineResultTest */
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class CoroutineResultTest : SysuiTestCase() {
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt
index 2f4fc96..bb400f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt
@@ -18,6 +18,7 @@
package com.android.systemui.common.ui.view
import android.view.ViewConfiguration
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.view.LongPressHandlingViewInteractionHandler.MotionEventModel
@@ -33,7 +34,6 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
@@ -41,7 +41,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class LongPressHandlingViewInteractionHandlerTest : SysuiTestCase() {
@Mock private lateinit var postDelayed: (Runnable, Long) -> DisposableHandle
diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java
index 4c4205e..cecb525 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java
@@ -25,12 +25,12 @@
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.SeekBar;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -49,7 +49,7 @@
* Tests for {@link SeekBarWithIconButtonsView}
*/
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class SeekBarWithIconButtonsViewTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java
index 288f3b6..ed21474 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java
@@ -21,10 +21,10 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.lifecycle.Observer;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -48,7 +48,7 @@
import java.util.HashSet;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class ComplicationCollectionLiveDataTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationHostViewControllerTest.java
index c43df17..dd3f991 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationHostViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationHostViewControllerTest.java
@@ -24,13 +24,13 @@
import android.os.UserHandle;
import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
import android.view.View;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -51,7 +51,7 @@
import java.util.HashSet;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class ComplicationHostViewControllerTest extends SysuiTestCase {
@Mock
ConstraintLayout mComplicationHostView;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutEngineTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutEngineTest.java
index baaeee1..383e0fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutEngineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutEngineTest.java
@@ -22,10 +22,10 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.testing.AndroidTestingRunner;
import android.view.View;
import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -48,7 +48,7 @@
import java.util.stream.Collectors;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class ComplicationLayoutEngineTest extends SysuiTestCase {
@Mock
ConstraintLayout mLayout;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutParamsTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutParamsTest.java
index a23e9e4..12cb8a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutParamsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutParamsTest.java
@@ -21,8 +21,8 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -36,7 +36,7 @@
import java.util.function.Consumer;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class ComplicationLayoutParamsTest extends SysuiTestCase {
/**
* Ensures ComplicationLayoutParams cannot be constructed with improper position or direction.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationTypesUpdaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationTypesUpdaterTest.java
index 8cd23b2..d728517 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationTypesUpdaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationTypesUpdaterTest.java
@@ -25,8 +25,8 @@
import android.database.ContentObserver;
import android.os.UserHandle;
import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.settingslib.dream.DreamBackend;
@@ -50,7 +50,7 @@
import java.util.HashSet;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class ComplicationTypesUpdaterTest extends SysuiTestCase {
@Mock
private Context mContext;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationUtilsTest.java
index e23e1f4..1e80233 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationUtilsTest.java
@@ -29,8 +29,8 @@
import static com.google.common.truth.Truth.assertThat;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.settingslib.dream.DreamBackend;
@@ -45,7 +45,7 @@
import java.util.Set;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class ComplicationUtilsTest extends SysuiTestCase {
@Test
public void testConvertComplicationType() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationViewModelTransformerTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationViewModelTransformerTest.java
index 09675e2..98b119a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationViewModelTransformerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationViewModelTransformerTest.java
@@ -21,9 +21,9 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.testing.AndroidTestingRunner;
import androidx.lifecycle.ViewModel;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -38,7 +38,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class ComplicationViewModelTransformerTest extends SysuiTestCase {
@Mock
ComplicationViewModelComponent.Factory mFactory;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamClockTimeComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamClockTimeComplicationTest.java
index b9aa4c6..22ab499 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamClockTimeComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamClockTimeComplicationTest.java
@@ -23,9 +23,9 @@
import static org.mockito.Mockito.when;
import android.content.Context;
-import android.testing.AndroidTestingRunner;
import android.view.View;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
@@ -42,7 +42,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class DreamClockTimeComplicationTest extends SysuiTestCase {
@SuppressWarnings("HidingField")
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java
index 18bd960b..ddf69b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java
@@ -29,9 +29,9 @@
import android.content.ComponentName;
import android.content.res.Resources;
-import android.testing.AndroidTestingRunner;
import android.view.View;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
@@ -62,7 +62,7 @@
import java.util.Optional;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class DreamHomeControlsComplicationTest extends SysuiTestCase {
@Mock
private DreamHomeControlsComplication mComplication;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java
index 05b4a41..3a856a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java
@@ -26,10 +26,10 @@
import android.app.PendingIntent;
import android.content.Intent;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.View;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.ActivityIntentHelper;
@@ -51,7 +51,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class DreamMediaEntryComplicationTest extends SysuiTestCase {
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/SmartSpaceComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/SmartSpaceComplicationTest.java
index 87de865..6c354ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/SmartSpaceComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/SmartSpaceComplicationTest.java
@@ -24,9 +24,9 @@
import static org.mockito.Mockito.when;
import android.app.smartspace.SmartspaceTarget;
-import android.testing.AndroidTestingRunner;
import android.view.View;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -52,7 +52,7 @@
import java.util.Set;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class SmartSpaceComplicationTest extends SysuiTestCase {
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt
index 03e4f9a..c2fe009 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt
@@ -17,10 +17,10 @@
package com.android.systemui.compose
import android.content.Context
-import android.testing.AndroidTestingRunner
import android.testing.ViewUtils
import android.widget.FrameLayout
import androidx.compose.ui.platform.ComposeView
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
@@ -28,7 +28,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class ComposeInitializerTest : SysuiTestCase() {
@Test
fun testCanAddComposeViewInInitializedWindow() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/CustomIconCacheTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/CustomIconCacheTest.kt
index 4d0f2ed..28e0cff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/CustomIconCacheTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/CustomIconCacheTest.kt
@@ -18,7 +18,7 @@
import android.content.ComponentName
import android.graphics.drawable.Icon
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import org.junit.Assert.assertNull
@@ -30,7 +30,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class CustomIconCacheTest : SysuiTestCase() {
companion object {
@@ -98,4 +98,4 @@
assertNull(customIconCache.retrieve(TEST_COMPONENT1, CONTROL_ID_1))
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapperTest.kt
index 129fe9a..8d6e3a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapperTest.kt
@@ -17,7 +17,7 @@
package com.android.systemui.controls.controller
import android.content.ComponentName
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import org.junit.Assert.assertEquals
@@ -37,7 +37,7 @@
import java.io.File
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class AuxiliaryPersistenceWrapperTest : SysuiTestCase() {
companion object {
@@ -128,4 +128,4 @@
verify(persistenceWrapper, never()).storeFavorites(ArgumentMatchers.anyList())
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
index 6cc3ef19..9285146 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
@@ -16,8 +16,8 @@
package com.android.systemui.controls.ui
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import android.testing.AndroidTestingRunner
import android.view.HapticFeedbackConstants
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastSender
@@ -48,7 +48,7 @@
import java.util.Optional
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class ControlActionCoordinatorImplTest : SysuiTestCase() {
@Mock
private lateinit var vibratorHelper: VibratorHelper
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
index 724c9d1..ed0c7ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
@@ -24,7 +24,7 @@
import android.service.controls.DeviceTypes
import android.service.controls.IControlsSubscriber
import android.service.controls.IControlsSubscription
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.settings.UserTracker
@@ -49,7 +49,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class ControlsBindingControllerImplTest : SysuiTestCase() {
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
index de455f63..cf385e7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
@@ -26,7 +26,7 @@
import android.service.controls.Control
import android.service.controls.DeviceTypes
import android.service.controls.actions.ControlAction
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.backup.BackupHelper
@@ -74,7 +74,7 @@
import java.util.function.Consumer
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class ControlsControllerImplTest : SysuiTestCase() {
private val kosmos = testKosmos()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt
index 690b9a7..afa5cec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt
@@ -18,7 +18,7 @@
import android.content.ComponentName
import android.service.controls.DeviceTypes
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.concurrency.FakeExecutor
@@ -32,7 +32,7 @@
import java.io.File
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class ControlsFavoritePersistenceWrapperTest : SysuiTestCase() {
private lateinit var file: File
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
index b5d3476..f9c2c6b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
@@ -26,7 +26,7 @@
import android.service.controls.IControlsSubscriber
import android.service.controls.actions.ControlAction
import android.service.controls.actions.ControlActionWrapper
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.concurrency.FakeExecutor
@@ -57,7 +57,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class ControlsProviderLifecycleManagerTest : SysuiTestCase() {
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt
index 581e88b..e04ce45 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt
@@ -16,7 +16,7 @@
package com.android.systemui.controls.controller
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
@@ -25,7 +25,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class ControlsTileResourceConfigurationImplTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/DeletionJobServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/DeletionJobServiceTest.kt
index 2283746..b6ea62e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/DeletionJobServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/DeletionJobServiceTest.kt
@@ -19,7 +19,7 @@
import android.app.job.JobParameters
import android.content.Context
import android.os.PersistableBundle
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.controller.AuxiliaryPersistenceWrapper.DeletionJobService.Companion.USER
@@ -37,7 +37,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class DeletionJobServiceTest : SysuiTestCase() {
@Mock private lateinit var context: Context
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/PackageUpdateMonitorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/PackageUpdateMonitorTest.kt
index 85d6211..282ea5c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/PackageUpdateMonitorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/PackageUpdateMonitorTest.kt
@@ -20,7 +20,7 @@
import android.content.pm.PackageManager
import android.os.Handler
import android.os.UserHandle
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.any
@@ -39,7 +39,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class PackageUpdateMonitorTest : SysuiTestCase() {
@Mock private lateinit var context: Context
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt
index 789d6df..b5c6c53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt
@@ -23,7 +23,7 @@
import android.service.controls.IControlsSubscription
import android.service.controls.actions.ControlAction
import android.service.controls.actions.ControlActionWrapper
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import org.junit.Assert.assertEquals
@@ -42,7 +42,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class ServiceWrapperTest : SysuiTestCase() {
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt
index 267520e..7d197f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt
@@ -20,7 +20,7 @@
import android.os.Binder
import android.service.controls.Control
import android.service.controls.IControlsSubscription
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.concurrency.FakeExecutor
@@ -36,7 +36,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class StatefulControlSubscriberTest : SysuiTestCase() {
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt
index 54f66dc..844cc1f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt
@@ -16,7 +16,7 @@
package com.android.systemui.controls.dagger
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED
@@ -45,7 +45,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class ControlsComponentTest : SysuiTestCase() {
@Mock private lateinit var controller: ControlsController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt
index 4ea9616..5528f65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt
@@ -19,7 +19,7 @@
import android.app.PendingIntent
import android.content.ComponentName
import android.service.controls.Control
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.ControlStatus
@@ -37,7 +37,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class AllModelTest : SysuiTestCase() {
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AppAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AppAdapterTest.kt
index 226ef3b..56c7c85 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AppAdapterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AppAdapterTest.kt
@@ -18,10 +18,10 @@
import android.content.ComponentName
import android.content.res.Resources
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.LayoutInflater
import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.core.lifecycle.Lifecycle
import com.android.systemui.SysuiTestCase
@@ -45,7 +45,7 @@
import java.text.Collator
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class AppAdapterTest : SysuiTestCase() {
private val fakeSystemClock = FakeSystemClock()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt
index 2a4524b..39e1e1d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt
@@ -3,12 +3,12 @@
import android.content.ComponentName
import android.content.Intent
import android.os.Bundle
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
import android.widget.Button
import android.window.OnBackInvokedCallback
import android.window.OnBackInvokedDispatcher
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.rule.ActivityTestRule
import com.android.systemui.res.R
@@ -33,7 +33,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class ControlsEditingActivityTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
index 88d36af..f5616d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
@@ -4,12 +4,12 @@
import android.content.Intent
import android.os.Bundle
import android.service.controls.Control
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
import android.widget.Button
import android.window.OnBackInvokedCallback
import android.window.OnBackInvokedDispatcher
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.FlakyTest
import androidx.test.filters.SmallTest
import androidx.test.rule.ActivityTestRule
@@ -45,7 +45,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class ControlsFavoritingActivityTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
index 6361e94..e4f0910 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
@@ -28,7 +28,7 @@
import android.os.Bundle
import android.os.UserHandle
import android.service.controls.ControlsProviderService
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.applications.ServiceListing
import com.android.systemui.res.R
@@ -65,7 +65,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class ControlsListingControllerImplTest : SysuiTestCase() {
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt
index d17495f..7698520 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt
@@ -23,10 +23,10 @@
import android.content.pm.ServiceInfo
import android.graphics.drawable.Drawable
import android.os.Bundle
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.window.OnBackInvokedCallback
import android.window.OnBackInvokedDispatcher
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.rule.ActivityTestRule
import com.android.systemui.SysuiTestCase
@@ -65,7 +65,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class ControlsProviderSelectorActivityTest : SysuiTestCase() {
@Main private val executor: Executor = MoreExecutors.directExecutor()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestDialogTest.kt
index ca970bb..5008927 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestDialogTest.kt
@@ -25,9 +25,9 @@
import android.service.controls.Control
import android.service.controls.ControlsProviderService
import android.service.controls.DeviceTypes
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.lifecycle.Lifecycle
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import androidx.test.rule.ActivityTestRule
import com.android.systemui.SysuiTestCase
@@ -53,7 +53,7 @@
import java.util.concurrent.Executor
@MediumTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class ControlsRequestDialogTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestReceiverTest.kt
index ae77d1f..c49867a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestReceiverTest.kt
@@ -30,7 +30,7 @@
import android.os.UserHandle
import android.service.controls.Control
import android.service.controls.ControlsProviderService
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import org.junit.Assert.assertEquals
@@ -49,7 +49,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class ControlsRequestReceiverTest : SysuiTestCase() {
@Mock
@@ -266,4 +266,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoritesModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoritesModelTest.kt
index f0003ed..281addc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoritesModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoritesModelTest.kt
@@ -17,8 +17,8 @@
package com.android.systemui.controls.management
import android.content.ComponentName
-import android.testing.AndroidTestingRunner
import androidx.recyclerview.widget.RecyclerView
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.ControlInterface
@@ -43,7 +43,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class FavoritesModelTest : SysuiTestCase() {
companion object {
@@ -299,4 +299,4 @@
}
private fun getDividerPosition(): Int = model.elements.indexOf(dividerWrapper)
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt
index 7f0ea9a..d8aac10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt
@@ -19,7 +19,7 @@
import android.content.Context
import android.content.DialogInterface
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.res.R
@@ -39,7 +39,7 @@
import org.mockito.Mockito.`when` as whenever
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class PanelConfirmationDialogFactoryTest : SysuiTestCase() {
@Mock private lateinit var mockDialog : SystemUIDialog
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt
index 18ce4a8..fd4c681 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt
@@ -19,7 +19,7 @@
import android.content.SharedPreferences
import android.content.pm.UserInfo
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -37,7 +37,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class AuthorizedPanelsRepositoryImplTest : SysuiTestCase() {
val kosmos = testKosmos()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/SelectedComponentRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/SelectedComponentRepositoryTest.kt
index a7e7ba9..86e3481 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/SelectedComponentRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/SelectedComponentRepositoryTest.kt
@@ -19,7 +19,7 @@
import android.content.ComponentName
import android.content.SharedPreferences
import android.os.UserHandle
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -43,7 +43,7 @@
import org.mockito.MockitoAnnotations
@ExperimentalCoroutinesApi
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class SelectedComponentRepositoryTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt
index 154c373..aee334f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt
@@ -22,8 +22,8 @@
import android.database.ContentObserver
import android.provider.Settings.Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS
import android.provider.Settings.Secure.LOCKSCREEN_SHOW_CONTROLS
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.settings.ControlsSettingsDialogManager.Companion.PREFS_SETTINGS_DIALOG_ATTEMPTS
@@ -52,7 +52,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class ControlsSettingsDialogManagerImplTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImplTest.kt
index b904ac1..3bdd5cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImplTest.kt
@@ -19,6 +19,7 @@
import android.content.pm.UserInfo
import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.user.data.repository.FakeUserRepository
@@ -33,10 +34,9 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
class ControlsSettingsRepositoryImplTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt
index c44429b..9e8914a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt
@@ -26,7 +26,7 @@
import android.content.pm.ServiceInfo
import android.os.UserHandle
import android.os.UserManager
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
@@ -75,7 +75,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class ControlsStartableTest : SysuiTestCase() {
private val kosmos = testKosmos()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt
index bfdb923..193ce21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt
@@ -21,7 +21,7 @@
import android.graphics.drawable.Icon
import android.net.Uri
import android.os.UserHandle
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
@@ -29,7 +29,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class CanUseIconPredicateTest : SysuiTestCase() {
private companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt
index 101b8ed..4b30fa5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt
@@ -24,11 +24,11 @@
import android.service.controls.Control
import android.service.controls.DeviceTypes
import android.service.controls.templates.ControlTemplate
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
@@ -44,7 +44,7 @@
import org.mockito.Mockito.mock
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class ControlViewHolderTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsActivityTest.kt
index e279d28..03aa622 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsActivityTest.kt
@@ -19,8 +19,8 @@
import android.content.Intent
import android.content.res.Configuration
import android.service.dreams.IDreamManager
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.rule.ActivityTestRule
import com.android.systemui.SysuiTestCase
@@ -39,7 +39,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class ControlsActivityTest : SysuiTestCase() {
@Mock private lateinit var uiController: ControlsUiController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt
index 38c6a0e..ca33f16 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt
@@ -18,7 +18,7 @@
package com.android.systemui.controls.ui
import android.content.Context
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
@@ -36,7 +36,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class ControlsDialogsFactoryTest : SysuiTestCase() {
private companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsPopupMenuTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsPopupMenuTest.kt
index 48e3962..66303eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsPopupMenuTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsPopupMenuTest.kt
@@ -19,12 +19,12 @@
import android.app.Activity
import android.graphics.Color
import android.graphics.drawable.ShapeDrawable
-import android.testing.AndroidTestingRunner
import android.util.DisplayMetrics
import android.view.View
import android.view.ViewGroup
import android.widget.PopupWindow.OnDismissListener
import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
@@ -43,7 +43,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
open class ControlsPopupMenuTest : SysuiTestCase() {
private companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
index 8f3813d..20890a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
@@ -26,13 +26,13 @@
import android.os.UserHandle
import android.service.controls.ControlsProviderService
import android.service.controls.flags.Flags.FLAG_HOME_PANEL_DREAM
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.ControlsMetricsLogger
@@ -83,7 +83,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class ControlsUiControllerImplTest : SysuiTestCase() {
private val kosmos = testKosmos()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt
index 677108c..10b3ce3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt
@@ -19,9 +19,9 @@
import android.app.ActivityOptions
import android.app.PendingIntent
import android.content.Context
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.activity.EmptyTestActivity
@@ -44,7 +44,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class DetailDialogTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt
index 483ab3b..6092b8c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt
@@ -17,7 +17,7 @@
package com.android.systemui.controls.ui
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
@@ -25,7 +25,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class OverflowMenuAdapterTest : SysuiTestCase() {
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/PanelTaskViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/PanelTaskViewControllerTest.kt
index 021facc..de2d852 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/PanelTaskViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/PanelTaskViewControllerTest.kt
@@ -25,8 +25,8 @@
import android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.graphics.Rect
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.boundsOnScreen
@@ -49,7 +49,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class PanelTaskViewControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/SelectionItemTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/SelectionItemTest.kt
index 57176f0..4579807 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/SelectionItemTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/SelectionItemTest.kt
@@ -1,7 +1,7 @@
package com.android.systemui.controls.ui
import android.content.ComponentName
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.controller.StructureInfo
@@ -11,7 +11,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class SelectionItemTest : SysuiTestCase() {
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ToggleRangeTemplateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ToggleRangeTemplateTest.kt
index 31e0954..9f4836a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ToggleRangeTemplateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ToggleRangeTemplateTest.kt
@@ -17,7 +17,7 @@
package com.android.systemui.controls.ui
import android.service.controls.templates.RangeTemplate
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import org.junit.Assert.assertEquals
@@ -25,7 +25,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class ToggleRangeTemplateTest : SysuiTestCase() {
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/coroutines/FlowTest.kt b/packages/SystemUI/tests/src/com/android/systemui/coroutines/FlowTest.kt
index 1e4753e..23da3f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/coroutines/FlowTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/coroutines/FlowTest.kt
@@ -1,6 +1,6 @@
package com.android.systemui.coroutines
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
@@ -12,7 +12,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class FlowTest : SysuiTestCase() {
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/CutoutDecorProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/CutoutDecorProviderFactoryTest.kt
index 1040ec4..f029847 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/decor/CutoutDecorProviderFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/decor/CutoutDecorProviderFactoryTest.kt
@@ -18,7 +18,6 @@
import android.graphics.Insets
import android.graphics.Rect
-import android.testing.AndroidTestingRunner
import android.testing.TestableResources
import android.util.RotationUtils
import android.util.Size
@@ -27,6 +26,7 @@
import android.view.DisplayCutout.BOUNDS_POSITION_LENGTH
import android.view.DisplayInfo
import android.view.Surface
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.any
@@ -38,7 +38,7 @@
import org.mockito.Mockito.doAnswer
import org.mockito.MockitoAnnotations
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class CutoutDecorProviderFactoryTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt
index a1cffc1..69fab56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt
@@ -17,11 +17,11 @@
package com.android.systemui.decor
import android.graphics.Color
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.DisplayCutout
import android.view.Surface
import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
@@ -34,7 +34,7 @@
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper(setAsMainLooper = true)
@SmallTest
class OverlayWindowTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt
index e4ddc37..6d6c6ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt
@@ -17,8 +17,8 @@
package com.android.systemui.decor
import android.content.res.Resources
-import android.testing.AndroidTestingRunner
import android.view.DisplayCutout
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
@@ -30,7 +30,7 @@
import org.mockito.Mockito.spy
import org.mockito.Mockito.`when` as whenever
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class PrivacyDotDecorProviderFactoryTest : SysuiTestCase() {
private lateinit var mPrivacyDotDecorProviderFactory: PrivacyDotDecorProviderFactory
@@ -83,4 +83,4 @@
and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_RIGHT))
})
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt
index d1d4880..4da988a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt
@@ -16,9 +16,9 @@
package com.android.systemui.decor
-import android.testing.AndroidTestingRunner
import android.util.Size
import android.view.DisplayCutout
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
@@ -30,7 +30,7 @@
import org.mockito.Mockito
import org.mockito.Mockito.spy
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class RoundedCornerDecorProviderFactoryTest : SysuiTestCase() {
@@ -139,4 +139,4 @@
})
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt
index 2bff7d2..9d440c3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt
@@ -18,9 +18,9 @@
import android.content.res.TypedArray
import android.graphics.drawable.Drawable
-import android.testing.AndroidTestingRunner
import android.util.Size
import androidx.annotation.DrawableRes
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.R as InternalR
import com.android.systemui.res.R as SystemUIR
@@ -33,7 +33,7 @@
import org.mockito.Mock
import org.mockito.MockitoAnnotations
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class RoundedCornerResDelegateTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt
index 6c2e136..4793a52f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt
@@ -18,8 +18,8 @@
import android.content.Intent
import android.os.Bundle
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.demomode.DemoMode.ACTION_DEMO
@@ -40,7 +40,7 @@
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@OptIn(ExperimentalCoroutinesApi::class)
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
@SmallTest
class DemoModeControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigTest.kt
index e9b4bbb..6b0de92 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigTest.kt
@@ -17,7 +17,7 @@
package com.android.systemui.deviceentry.data.repository
import android.os.PowerManager
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
@@ -30,7 +30,7 @@
import org.mockito.Mock
import org.mockito.MockitoAnnotations
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class FaceWakeUpTriggersConfigTest : SysuiTestCase() {
@Mock lateinit var globalSettings: GlobalSettings
diff --git a/packages/SystemUI/tests/src/com/android/systemui/devicepolicy/DevicePolicyManagerExtTest.kt b/packages/SystemUI/tests/src/com/android/systemui/devicepolicy/DevicePolicyManagerExtTest.kt
index 8203291..64ff5f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/devicepolicy/DevicePolicyManagerExtTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/devicepolicy/DevicePolicyManagerExtTest.kt
@@ -22,6 +22,7 @@
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.settings.UserTracker
@@ -31,13 +32,12 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class DevicePolicyManagerExtTest : SysuiTestCase() {
@Mock lateinit var devicePolicyManager: DevicePolicyManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DeviceStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DeviceStateRepositoryTest.kt
index c79cbab..3f5b9a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DeviceStateRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DeviceStateRepositoryTest.kt
@@ -17,8 +17,8 @@
package com.android.systemui.display.data.repository
import android.hardware.devicestate.DeviceStateManager
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.R
import com.android.systemui.SysuiTestCase
@@ -41,7 +41,7 @@
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
index 68d49c7..01868ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
@@ -18,11 +18,11 @@
import android.hardware.display.DisplayManager
import android.os.Looper
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.Display
import android.view.Display.TYPE_EXTERNAL
import android.view.Display.TYPE_INTERNAL
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.FlowValue
@@ -46,7 +46,7 @@
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt
index 37c7409..fd9964f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt
@@ -19,12 +19,12 @@
import android.companion.virtual.VirtualDeviceManager
import android.companion.virtual.flags.Flags.FLAG_INTERACTIVE_SCREEN_MIRROR
import android.platform.test.annotations.EnableFlags
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.Display
import android.view.Display.TYPE_EXTERNAL
import android.view.Display.TYPE_INTERNAL
import android.view.Display.TYPE_VIRTUAL
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.FlowValue
@@ -53,7 +53,7 @@
import org.junit.runner.RunWith
import org.mockito.Mockito.anyInt
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogDelegateTest.kt
index d118cc7..8105bc8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogDelegateTest.kt
@@ -18,13 +18,13 @@
import android.app.Dialog
import android.graphics.Insets
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.LayoutInflater
import android.view.View
import android.view.Window
import android.view.WindowInsets
import android.view.WindowInsetsAnimation
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.app.animation.Interpolators
import com.android.systemui.SysuiTestCase
@@ -42,7 +42,7 @@
import org.mockito.Mockito.verify
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class MirroringConfirmationDialogDelegateTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
index 6d2df19..8c125f8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
@@ -28,9 +28,9 @@
import android.app.ActivityManager;
import android.hardware.display.AmbientDisplayConfiguration;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -46,7 +46,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@RunWithLooper
public class DozeDockHandlerTest extends SysuiTestCase {
@Mock private DozeMachine mMachine;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index 27fd3b1..aa5edae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -45,8 +45,8 @@
import android.os.PowerManager;
import android.os.UserHandle;
import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -71,7 +71,7 @@
import java.util.Optional;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class DozeScreenBrightnessTest extends SysuiTestCase {
private static final int DEFAULT_BRIGHTNESS = 10;
@@ -583,4 +583,4 @@
private void waitForSensorManager() {
mFakeExecutor.runAllReady();
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
index 3cc0451..9c127b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -42,10 +42,10 @@
import android.database.ContentObserver;
import android.hardware.Sensor;
import android.hardware.display.AmbientDisplayConfiguration;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -75,7 +75,7 @@
import java.util.List;
import java.util.function.Consumer;
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@RunWithLooper
@SmallTest
public class DozeSensorsTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java
index 92941f9..fad52e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java
@@ -38,9 +38,9 @@
import android.app.ActivityManager;
import android.hardware.display.AmbientDisplayConfiguration;
-import android.testing.AndroidTestingRunner;
import android.testing.UiThreadTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -60,7 +60,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@UiThreadTest
public class DozeSuppressorTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index 40b8fc7..3d1a0d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -38,10 +38,10 @@
import android.hardware.Sensor;
import android.hardware.display.AmbientDisplayConfiguration;
import android.platform.test.annotations.EnableFlags;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.view.Display;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.InstanceId;
@@ -80,7 +80,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@RunWithLooper(setAsMainLooper = true)
public class DozeTriggersTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
index f07edf3..4253c76 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
@@ -26,6 +26,7 @@
import android.app.IWallpaperManager;
import android.os.RemoteException;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -36,11 +37,10 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RunWith(JUnit4.class)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class DozeWallpaperStateTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
index 2bd2bff..771ecaf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
@@ -16,7 +16,7 @@
package com.android.systemui.flags
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
@@ -25,7 +25,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class FakeFeatureFlagsTest : SysuiTestCase() {
private val unreleasedFlag = UnreleasedFlag("-1000", "test")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagDependenciesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagDependenciesTest.kt
index 91da88e..0ae59bb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagDependenciesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagDependenciesTest.kt
@@ -16,8 +16,8 @@
package com.android.systemui.flags
-import android.testing.AndroidTestingRunner
import android.util.Log
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import java.io.PrintWriter
@@ -25,7 +25,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class FlagDependenciesTest : SysuiTestCase() {
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/ServerFlagReaderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/ServerFlagReaderImplTest.kt
index 2daa86b..d1082bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/ServerFlagReaderImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/ServerFlagReaderImplTest.kt
@@ -16,7 +16,7 @@
package com.android.systemui.flags
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.DeviceConfigProxyFake
@@ -33,7 +33,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class ServerFlagReaderImplTest : SysuiTestCase() {
private val NAMESPACE = "test"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 6b1d39a..03afcb7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -1240,6 +1240,7 @@
mViewMediator.start();
mViewMediator.registerCentralSurfaces(mCentralSurfaces, null, null, null, null);
+ mViewMediator.onBootCompleted();
}
private void captureKeyguardStateControllerCallback() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
index 7c92ede..42ab25f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
@@ -48,6 +48,7 @@
import com.android.systemui.kosmos.testScope
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
import com.android.systemui.testKosmos
import kotlin.test.Test
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -78,6 +79,9 @@
fun setup() {
underTest.start()
+ kosmos.fakeKeyguardRepository.setDreaming(true)
+ kosmos.keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(true)
+
// Transition to DOZING and set the power interactor asleep.
powerInteractor.setAsleepForTest()
runBlocking {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt
index 88fe4ce..af76b08 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt
@@ -16,12 +16,18 @@
package com.android.systemui.keyguard.domain.interactor
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.AuthenticationFlags
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -81,6 +87,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
fun testTransitionsToLockscreen_ifFinishedInGone() =
testScope.runTest {
keyguardTransitionRepository.sendTransitionSteps(
@@ -92,7 +99,34 @@
kosmos.fakeKeyguardRepository.setKeyguardShowing(true)
runCurrent()
- // We're in the middle of a LOCKSCREEN -> GONE transition.
+ // We're in the middle of a GONE -> LOCKSCREEN transition.
+ assertThat(keyguardTransitionRepository)
+ .startedTransition(
+ to = KeyguardState.LOCKSCREEN,
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testTransitionsToLockscreen_ifFinishedInGone_wmRefactor() =
+ testScope.runTest {
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ testScope,
+ )
+ reset(keyguardTransitionRepository)
+
+ // Trigger lockdown.
+ kosmos.fakeBiometricSettingsRepository.setAuthenticationFlags(
+ AuthenticationFlags(
+ 0,
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN
+ )
+ )
+ runCurrent()
+
+ // We're in the middle of a GONE -> LOCKSCREEN transition.
assertThat(keyguardTransitionRepository)
.startedTransition(
to = KeyguardState.LOCKSCREEN,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt
index c782e9d..459e41d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt
@@ -32,6 +32,7 @@
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.util.mockTopActivityClassName
import com.android.systemui.shared.system.ActivityManagerWrapper
+import com.android.systemui.user.domain.UserDomainLayerModule
import dagger.BindsInstance
import dagger.Component
import junit.framework.Assert.assertEquals
@@ -443,6 +444,7 @@
[
SysUITestModule::class,
BiometricsDomainLayerModule::class,
+ UserDomainLayerModule::class,
]
)
interface TestComponent {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index e02fb29..246cfbf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -30,7 +30,6 @@
import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.communal.domain.interactor.setCommunalAvailable
import com.android.systemui.communal.shared.model.CommunalScenes
-import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.dock.fakeDockManager
import com.android.systemui.flags.BrokenWithSceneContainer
import com.android.systemui.flags.DisableSceneContainer
@@ -570,13 +569,13 @@
// WHEN biometrics succeeds with wake and unlock mode
powerInteractor.setAwakeForTest()
keyguardRepository.setBiometricUnlockState(BiometricUnlockMode.WAKE_AND_UNLOCK)
- advanceTimeBy(60L)
+ runCurrent()
assertThat(transitionRepository)
.startedTransition(
to = KeyguardState.GONE,
from = KeyguardState.DOZING,
- ownerName = "FromDozingTransitionInteractor",
+ ownerName = "FromDozingTransitionInteractor(biometric wake and unlock)",
animatorAssertion = { it.isNotNull() }
)
@@ -608,31 +607,6 @@
/** This handles security method NONE and screen off with lock timeout */
@Test
- fun dozingToGoneWithKeyguardNotShowing() =
- testScope.runTest {
- // GIVEN a prior transition has run to DOZING
- runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.DOZING)
- runCurrent()
-
- // WHEN the device wakes up without a keyguard
- keyguardRepository.setKeyguardShowing(false)
- keyguardRepository.setKeyguardDismissible(true)
- kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(false)
- powerInteractor.setAwakeForTest()
- advanceTimeBy(60L)
-
- assertThat(transitionRepository)
- .startedTransition(
- to = KeyguardState.GONE,
- from = KeyguardState.DOZING,
- animatorAssertion = { it.isNotNull() }
- )
-
- coroutineContext.cancelChildren()
- }
-
- /** This handles security method NONE and screen off with lock timeout */
- @Test
@DisableSceneContainer
fun dreamingToGoneWithKeyguardNotShowing() =
testScope.runTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt
index 33e9b36..c7f4416 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt
@@ -24,6 +24,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager
import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController
+import com.android.systemui.user.domain.UserDomainLayerModule
import com.android.systemui.util.mockito.any
import dagger.BindsInstance
import dagger.Component
@@ -120,6 +121,7 @@
[
SysUITestModule::class,
BiometricsDomainLayerModule::class,
+ UserDomainLayerModule::class,
]
)
interface TestComponent {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
index 1f13298..4e1b12f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
@@ -100,9 +100,11 @@
}
@Test
- fun testAodVisible_noLockscreenShownCallYet_defaultsToShowLockscreen() {
+ fun testAodVisible_noLockscreenShownCallYet_doesNotShowLockscreenUntilLater() {
underTest.setAodVisible(false)
+ verifyNoMoreInteractions(activityTaskManagerService)
+ underTest.setLockscreenShown(true)
verify(activityTaskManagerService).setLockScreenShown(true, false)
verifyNoMoreInteractions(activityTaskManagerService)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
index 8471fe1..064cf09 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
@@ -663,6 +663,7 @@
true
)
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
+ repository.setOrderedMedia()
assertThat(currentMedia).containsExactly(controlCommonModel)
verify(listener)
@@ -706,6 +707,7 @@
true
)
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
+ repository.setOrderedMedia()
assertThat(currentMedia).containsExactly(controlCommonModel)
verify(listener)
.onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
@@ -760,6 +762,7 @@
)
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
+ repository.setOrderedMedia()
assertThat(currentMedia).containsExactly(controlCommonModel)
verify(listener)
@@ -834,6 +837,7 @@
)
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
+ repository.setOrderedMedia()
assertThat(currentMedia).containsExactly(controlCommonModel)
verify(listener)
@@ -922,6 +926,7 @@
// If there is media that was recently played but inactive
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
+ repository.setOrderedMedia()
verify(listener)
.onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
@@ -986,6 +991,7 @@
// WHEN we have media that was recently played, but not currently active
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
+ repository.setOrderedMedia()
verify(listener)
.onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
@@ -1039,6 +1045,7 @@
// WHEN we have media that was recently played, but not currently active
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
+ repository.setOrderedMedia()
verify(listener)
.onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
index 18b4c48..3b541cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
@@ -242,7 +242,6 @@
mediaCarouselInteractor =
MediaCarouselInteractor(
applicationScope = testScope.backgroundScope,
- mediaDataRepository = mediaDataRepository,
mediaDataProcessor = mediaDataProcessor,
mediaTimeoutListener = mediaTimeoutListener,
mediaResumeListener = mediaResumeListener,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
index 42bd46f..5142730 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
@@ -21,6 +21,7 @@
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
+import android.graphics.drawable.TestStubDrawable
import android.media.MediaRoute2Info
import android.media.MediaRouter2Manager
import android.media.RoutingSessionInfo
@@ -30,6 +31,7 @@
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.annotations.RequiresFlagsEnabled
import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -89,6 +91,11 @@
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
public class MediaDeviceManagerTest : SysuiTestCase() {
+
+ private companion object {
+ val OTHER_DEVICE_ICON_STUB = TestStubDrawable()
+ }
+
@get:Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
private lateinit var manager: MediaDeviceManager
@@ -155,6 +162,11 @@
MediaTestUtils.emptyMediaData.copy(packageName = PACKAGE, token = session.sessionToken)
whenever(controllerFactory.create(session.sessionToken)).thenReturn(controller)
setupLeAudioConfiguration(false)
+
+ context.orCreateTestableResources.addOverride(
+ R.drawable.ic_media_home_devices,
+ OTHER_DEVICE_ICON_STUB
+ )
}
@After
@@ -414,6 +426,7 @@
assertThat(data.name).isEqualTo(REMOTE_DEVICE_NAME)
}
+ @RequiresFlagsDisabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
@Test
fun onMediaDataLoaded_withRemotePlaybackInfo_noMatchingRoutingSession_setsDisabledDevice() {
// GIVEN that MR2Manager returns null for routing session
@@ -429,6 +442,24 @@
assertThat(data.name).isNull()
}
+ @RequiresFlagsEnabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
+ @Test
+ fun onMediaDataLoaded_withRemotePlaybackInfo_noMatchingRoutingSession_returnsOtherDevice() {
+ // GIVEN that MR2Manager returns null for routing session
+ whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
+ whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
+ // WHEN a notification is added
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ // THEN the device is disabled and name and icon are set to "OTHER DEVICE".
+ val data = captureDeviceData(KEY)
+ assertThat(data.enabled).isFalse()
+ assertThat(data.name).isEqualTo(context.getString(R.string.media_seamless_other_device))
+ assertThat(data.icon).isEqualTo(OTHER_DEVICE_ICON_STUB)
+ }
+
+ @RequiresFlagsDisabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
@Test
fun onSelectedDeviceStateChanged_withRemotePlaybackInfo_noMatchingRoutingSession_setsDisabledDevice() {
// GIVEN a notif is added
@@ -449,7 +480,30 @@
assertThat(data.enabled).isFalse()
assertThat(data.name).isNull()
}
+ @RequiresFlagsEnabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
+ @Test
+ fun onSelectedDeviceStateChanged_withRemotePlaybackInfo_noMatchingRoutingSession_returnOtherDevice() {
+ // GIVEN a notif is added
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ reset(listener)
+ // AND MR2Manager returns null for routing session
+ whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
+ whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
+ // WHEN the selected device changes state
+ val deviceCallback = captureCallback()
+ deviceCallback.onSelectedDeviceStateChanged(device, 1)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ // THEN the device is disabled and name and icon are set to "OTHER DEVICE".
+ val data = captureDeviceData(KEY)
+ assertThat(data.enabled).isFalse()
+ assertThat(data.name).isEqualTo(context.getString(R.string.media_seamless_other_device))
+ assertThat(data.icon).isEqualTo(OTHER_DEVICE_ICON_STUB)
+ }
+ @RequiresFlagsDisabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
@Test
fun onDeviceListUpdate_withRemotePlaybackInfo_noMatchingRoutingSession_setsDisabledDevice() {
// GIVEN a notif is added
@@ -471,6 +525,29 @@
assertThat(data.name).isNull()
}
+ @RequiresFlagsEnabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
+ @Test
+ fun onDeviceListUpdate_withRemotePlaybackInfo_noMatchingRoutingSession_returnsOtherDevice() {
+ // GIVEN a notif is added
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ reset(listener)
+ // GIVEN that MR2Manager returns null for routing session
+ whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
+ whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
+ // WHEN the selected device changes state
+ val deviceCallback = captureCallback()
+ deviceCallback.onDeviceListUpdate(mutableListOf(device))
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ // THEN device is disabled and name and icon are set to "OTHER DEVICE".
+ val data = captureDeviceData(KEY)
+ assertThat(data.enabled).isFalse()
+ assertThat(data.name).isEqualTo(context.getString(R.string.media_seamless_other_device))
+ assertThat(data.icon).isEqualTo(OTHER_DEVICE_ICON_STUB)
+ }
+
// With the flag enabled, MediaDeviceManager no longer gathers device name information directly.
@RequiresFlagsDisabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
index 6a2637d..ccf926a 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
@@ -193,11 +193,12 @@
whenever(panel.mediaViewController).thenReturn(mediaViewController)
whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(false)
MediaPlayerData.clear()
+ FakeExecutor.exhaustExecutors(bgExecutor)
verify(globalSettings)
- .registerContentObserverSync(
- eq(Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE)),
- capture(settingsObserverCaptor)
- )
+ .registerContentObserverSync(
+ eq(Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE)),
+ capture(settingsObserverCaptor)
+ )
}
@After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
index bba01bd..6c350cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
@@ -25,6 +25,8 @@
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardViewController
import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
+import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.ui.viewmodel.communalTransitionViewModel
import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq
import com.android.systemui.dreams.DreamOverlayStateController
@@ -498,6 +500,8 @@
to = KeyguardState.GLANCEABLE_HUB,
testScope = testScope,
)
+ kosmos.fakeCommunalSceneRepository.changeScene(CommunalScenes.Communal)
+ runCurrent()
mediaHierarchyManager.qsExpansion = 0f
mediaHierarchyManager.setTransitionToFullShadeAmount(123f)
@@ -542,6 +546,8 @@
to = KeyguardState.GLANCEABLE_HUB,
testScope = testScope,
)
+ kosmos.fakeCommunalSceneRepository.changeScene(CommunalScenes.Communal)
+ runCurrent()
verify(mediaCarouselController)
.onDesiredLocationChanged(
eq(MediaHierarchyManager.LOCATION_COMMUNAL_HUB),
@@ -557,6 +563,8 @@
to = KeyguardState.LOCKSCREEN,
testScope = testScope,
)
+ kosmos.fakeCommunalSceneRepository.changeScene(CommunalScenes.Blank)
+ runCurrent()
verify(mediaCarouselController)
.onDesiredLocationChanged(
eq(MediaHierarchyManager.LOCATION_QQS),
@@ -579,6 +587,8 @@
to = KeyguardState.GLANCEABLE_HUB,
testScope = testScope,
)
+ kosmos.fakeCommunalSceneRepository.changeScene(CommunalScenes.Communal)
+ runCurrent()
verify(mediaCarouselController)
.onDesiredLocationChanged(
eq(MediaHierarchyManager.LOCATION_COMMUNAL_HUB),
@@ -600,6 +610,8 @@
to = KeyguardState.GLANCEABLE_HUB,
testScope = testScope,
)
+ kosmos.fakeCommunalSceneRepository.changeScene(CommunalScenes.Communal)
+ runCurrent()
verify(mediaCarouselController)
.onDesiredLocationChanged(
eq(MediaHierarchyManager.LOCATION_COMMUNAL_HUB),
@@ -635,6 +647,8 @@
to = KeyguardState.GLANCEABLE_HUB,
testScope = testScope,
)
+ kosmos.fakeCommunalSceneRepository.changeScene(CommunalScenes.Communal)
+ runCurrent()
// Mock the behavior for dreaming that pulling down shade will immediately set QS as
// expanded
expandQS()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index ec02c64..411ff91 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -113,8 +113,8 @@
LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED);
mMediaDevices.add(mMediaDevice1);
mMediaDevices.add(mMediaDevice2);
- mMediaItems.add(new MediaItem(mMediaDevice1));
- mMediaItems.add(new MediaItem(mMediaDevice2));
+ mMediaItems.add(MediaItem.createDeviceMediaItem(mMediaDevice1));
+ mMediaItems.add(MediaItem.createDeviceMediaItem(mMediaDevice2));
mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController);
mMediaOutputAdapter.updateItems();
@@ -146,7 +146,8 @@
mMediaOutputAdapter.updateItems();
mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
.onCreateViewHolder(new LinearLayout(mContext), 0);
- mMediaItems.add(new MediaItem());
+ mMediaItems.add(MediaItem.createPairNewDeviceMediaItem());
+ mMediaItems.add(MediaItem.createPairNewDeviceMediaItem());
mMediaOutputAdapter.updateItems();
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2);
@@ -589,7 +590,7 @@
mMediaOutputAdapter.updateItems();
mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
.onCreateViewHolder(new LinearLayout(mContext), 0);
- mMediaItems.add(new MediaItem());
+ mMediaItems.add(MediaItem.createPairNewDeviceMediaItem());
mMediaOutputAdapter.updateItems();
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2);
mViewHolder.mContainerLayout.performClick();
@@ -725,7 +726,7 @@
public void updateItems_controllerItemsUpdated_notUpdatesInAdapterUntilUpdateItems() {
mMediaOutputAdapter.updateItems();
List<MediaItem> updatedList = new ArrayList<>();
- updatedList.add(new MediaItem());
+ updatedList.add(MediaItem.createPairNewDeviceMediaItem());
when(mMediaOutputController.getMediaItemList()).thenReturn(updatedList);
assertThat(mMediaOutputAdapter.getItemCount()).isEqualTo(mMediaItems.size());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt
index 53e9dc8..2a8967e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt
@@ -62,7 +62,7 @@
MockitoAnnotations.initMocks(this)
context.addMockSystemService(Context.AUDIO_SERVICE, audioManager)
icon = context.getDrawable(R.drawable.ic_cake)!!
- whenever(deviceIconUtil.getIconFromAudioDeviceType(any(), any())).thenReturn(icon)
+ whenever(deviceIconUtil.getIconFromAudioDeviceType(any())).thenReturn(icon)
muteAwaitConnectionManager = MediaMuteAwaitConnectionManager(
FakeExecutor(FakeSystemClock()),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt
index b7fefc0..c0d411b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt
@@ -16,7 +16,9 @@
package com.android.systemui.mediaprojection.data.repository
+import android.media.projection.MediaProjectionInfo
import android.os.Binder
+import android.os.UserHandle
import android.view.ContentRecordingSession
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -26,6 +28,7 @@
import com.android.systemui.mediaprojection.data.model.MediaProjectionState
import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createToken
+import com.android.systemui.mediaprojection.taskswitcher.FakeMediaProjectionManager.Companion.createDisplaySession
import com.android.systemui.mediaprojection.taskswitcher.fakeActivityTaskManager
import com.android.systemui.mediaprojection.taskswitcher.fakeMediaProjectionManager
import com.android.systemui.mediaprojection.taskswitcher.taskSwitcherKosmos
@@ -33,6 +36,7 @@
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.verify
@RunWith(AndroidJUnit4::class)
@SmallTest
@@ -55,7 +59,8 @@
fakeActivityTaskManager.addRunningTasks(task)
repo.switchProjectedTask(task)
- assertThat(state).isEqualTo(MediaProjectionState.SingleTask(task))
+ assertThat(state).isInstanceOf(MediaProjectionState.Projecting.SingleTask::class.java)
+ assertThat((state as MediaProjectionState.Projecting.SingleTask).task).isEqualTo(task)
}
@Test
@@ -97,7 +102,7 @@
session = ContentRecordingSession.createDisplaySession(/* displayToMirror= */ 123)
)
- assertThat(state).isEqualTo(MediaProjectionState.EntireScreen)
+ assertThat(state).isInstanceOf(MediaProjectionState.Projecting.EntireScreen::class.java)
}
@Test
@@ -110,7 +115,27 @@
session = ContentRecordingSession.createTaskSession(taskWindowContainerToken)
)
- assertThat(state).isEqualTo(MediaProjectionState.EntireScreen)
+ assertThat(state).isInstanceOf(MediaProjectionState.Projecting.EntireScreen::class.java)
+ }
+
+ @Test
+ fun mediaProjectionState_entireScreen_hasHostPackage() =
+ testScope.runTest {
+ val state by collectLastValue(repo.mediaProjectionState)
+
+ val info =
+ MediaProjectionInfo(
+ /* packageName= */ "com.media.projection.repository.test",
+ /* handle= */ UserHandle.getUserHandleForUid(UserHandle.myUserId()),
+ /* launchCookie = */ null,
+ )
+ fakeMediaProjectionManager.dispatchOnSessionSet(
+ info = info,
+ session = createDisplaySession(),
+ )
+
+ assertThat((state as MediaProjectionState.Projecting.EntireScreen).hostPackage)
+ .isEqualTo("com.media.projection.repository.test")
}
@Test
@@ -125,6 +150,39 @@
session = ContentRecordingSession.createTaskSession(token.asBinder())
)
- assertThat(state).isEqualTo(MediaProjectionState.SingleTask(task))
+ assertThat(state).isInstanceOf(MediaProjectionState.Projecting.SingleTask::class.java)
+ assertThat((state as MediaProjectionState.Projecting.SingleTask).task).isEqualTo(task)
+ }
+
+ @Test
+ fun mediaProjectionState_singleTask_hasHostPackage() =
+ testScope.runTest {
+ val state by collectLastValue(repo.mediaProjectionState)
+
+ val token = createToken()
+ val task = createTask(taskId = 1, token = token)
+ fakeActivityTaskManager.addRunningTasks(task)
+
+ val info =
+ MediaProjectionInfo(
+ /* packageName= */ "com.media.projection.repository.test",
+ /* handle= */ UserHandle.getUserHandleForUid(UserHandle.myUserId()),
+ /* launchCookie = */ null,
+ )
+ fakeMediaProjectionManager.dispatchOnSessionSet(
+ info = info,
+ session = ContentRecordingSession.createTaskSession(token.asBinder())
+ )
+
+ assertThat((state as MediaProjectionState.Projecting.SingleTask).hostPackage)
+ .isEqualTo("com.media.projection.repository.test")
+ }
+
+ @Test
+ fun stopProjecting_invokesManager() =
+ testScope.runTest {
+ repo.stopProjecting()
+
+ verify(fakeMediaProjectionManager.mediaProjectionManager).stopActiveProjection()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
index 130aafb..415cc7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
@@ -28,11 +28,12 @@
import android.widget.TextView
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.haptics.qs.QSLongPressEffect
import com.android.systemui.haptics.qs.qsLongPressEffect
import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.qsTileFactory
+import com.android.systemui.res.R
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -46,8 +47,7 @@
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class QSTileViewImplTest : SysuiTestCase() {
- @Mock
- private lateinit var customDrawable: Drawable
+ @Mock private lateinit var customDrawable: Drawable
private lateinit var tileView: FakeTileView
private lateinit var customDrawableView: View
@@ -130,9 +130,8 @@
tileView.changeState(state)
- assertThat(state.secondaryLabel as CharSequence).isEqualTo(
- context.getString(R.string.tile_unavailable)
- )
+ assertThat(state.secondaryLabel as CharSequence)
+ .isEqualTo(context.getString(R.string.tile_unavailable))
}
@Test
@@ -143,9 +142,8 @@
tileView.changeState(state)
- assertThat(state.secondaryLabel as CharSequence).isEqualTo(
- context.getString(R.string.switch_bar_off)
- )
+ assertThat(state.secondaryLabel as CharSequence)
+ .isEqualTo(context.getString(R.string.switch_bar_off))
}
@Test
@@ -156,9 +154,8 @@
tileView.changeState(state)
- assertThat(state.secondaryLabel as CharSequence).isEqualTo(
- context.getString(R.string.switch_bar_on)
- )
+ assertThat(state.secondaryLabel as CharSequence)
+ .isEqualTo(context.getString(R.string.switch_bar_on))
}
@Test
@@ -236,11 +233,10 @@
val offString = "${spec}_off"
val onString = "${spec}_on"
- context.orCreateTestableResources.addOverride(R.array.tile_states_internet, arrayOf(
- unavailableString,
- offString,
- onString
- ))
+ context.orCreateTestableResources.addOverride(
+ R.array.tile_states_internet,
+ arrayOf(unavailableString, offString, onString)
+ )
// State UNAVAILABLE
state.secondaryLabel = ""
@@ -342,11 +338,10 @@
@Test
fun testDisabledByPolicy_secondaryLabelText() {
val testA11yLabel = "TEST_LABEL"
- context.orCreateTestableResources
- .addOverride(
- R.string.accessibility_tile_disabled_by_policy_action_description,
- testA11yLabel
- )
+ context.orCreateTestableResources.addOverride(
+ R.string.accessibility_tile_disabled_by_policy_action_description,
+ testA11yLabel
+ )
val stateDisabledByPolicy = QSTile.State()
stateDisabledByPolicy.state = Tile.STATE_INACTIVE
@@ -357,10 +352,11 @@
val info = AccessibilityNodeInfo(tileView)
tileView.onInitializeAccessibilityNodeInfo(info)
assertThat(
- info.actionList.find {
- it.id == AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.id
- }?.label
- ).isEqualTo(testA11yLabel)
+ info.actionList
+ .find { it.id == AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.id }
+ ?.label
+ )
+ .isEqualTo(testA11yLabel)
}
@Test
@@ -375,11 +371,10 @@
val offString = "${spec}_off"
val onString = "${spec}_on"
- context.orCreateTestableResources.addOverride(R.array.tile_states_internet, arrayOf(
- unavailableString,
- offString,
- onString
- ))
+ context.orCreateTestableResources.addOverride(
+ R.array.tile_states_internet,
+ arrayOf(unavailableString, offString, onString)
+ )
tileView.changeState(state)
assertThat(tileView.stateDescription?.contains(unavailableString)).isTrue()
@@ -412,7 +407,7 @@
}
@Test
- fun onStateChange_fromLongPress_to_noLongPress_unBoundsTile() {
+ fun onStateChange_fromLongPress_to_noLongPress_clearsResources() {
// GIVEN a state that no longer handles long-press
val state = QSTile.State()
state.handlesLongClick = false
@@ -420,12 +415,12 @@
// WHEN the state changes
tileView.changeState(state)
- // THEN the view binder no longer binds the view to the long-press effect
- assertThat(tileView.isLongPressEffectBound).isFalse()
+ // THEN the long-press effect resources are not set
+ assertThat(tileView.areLongPressEffectPropertiesSet).isFalse()
}
@Test
- fun onStateChange_fromNoLongPress_to_longPress_bindsTile() {
+ fun onStateChange_fromNoLongPress_to_longPress_setsProperties() {
// GIVEN that the tile has changed to a state that does not handle long-press
val state = QSTile.State()
state.handlesLongClick = false
@@ -435,12 +430,12 @@
state.handlesLongClick = true
tileView.changeState(state)
- // THEN the view is bounded to the long-press effect
- assertThat(tileView.isLongPressEffectBound).isTrue()
+ // THEN the long-press effect resources are set
+ assertThat(tileView.areLongPressEffectPropertiesSet).isTrue()
}
@Test
- fun onStateChange_withoutLongPressEffect_fromLongPress_to_noLongPress_neverBindsEffect() {
+ fun onStateChange_withoutLongPressEffect_fromLongPress_to_noLongPress_neverSetsProperties() {
// GIVEN a tile where the long-press effect is null
tileView = FakeTileView(context, false, null)
@@ -451,13 +446,13 @@
// WHEN the state changes
tileView.changeState(state)
- // THEN the view binder does not bind the view and no effect is initialized
- assertThat(tileView.isLongPressEffectBound).isFalse()
+ // THEN the effect properties are not set and the effect is not initialized
+ assertThat(tileView.areLongPressEffectPropertiesSet).isFalse()
assertThat(tileView.isLongPressEffectInitialized).isFalse()
}
@Test
- fun onStateChange_withoutLongPressEffect_fromNoLongPress_to_longPress_neverBindsEffect() {
+ fun onStateChange_withoutLongPressEffect_fromNoLongPress_to_longPress_neverSetsProperties() {
// GIVEN a tile where the long-press effect is null
tileView = FakeTileView(context, false, null)
@@ -470,8 +465,8 @@
state.handlesLongClick = true
tileView.changeState(state)
- // THEN the view binder does not bind the view and no effect is initialized
- assertThat(tileView.isLongPressEffectBound).isFalse()
+ // THEN the effect properties are not set and the effect is not initialized
+ assertThat(tileView.areLongPressEffectPropertiesSet).isFalse()
assertThat(tileView.isLongPressEffectInitialized).isFalse()
}
@@ -490,14 +485,15 @@
// THE animation padding corresponds to the tile's growth due to the effect
val padding = tileView.getPaddingForLaunchAnimation()
- assertThat(padding).isEqualTo(
- Rect(
- -deltaWidth.toInt() / 2,
- -deltaHeight.toInt() / 2,
- deltaWidth.toInt() / 2,
- deltaHeight.toInt() / 2,
+ assertThat(padding)
+ .isEqualTo(
+ Rect(
+ -deltaWidth.toInt() / 2,
+ -deltaHeight.toInt() / 2,
+ deltaWidth.toInt() / 2,
+ deltaHeight.toInt() / 2,
+ )
)
- )
}
@Test
@@ -536,18 +532,44 @@
assertThat(tileView.haveLongPressPropertiesBeenReset).isTrue()
}
+ @Test
+ fun onInit_withLongPressEffect_longPressEffectHasTileAndExpandable() {
+ val tile = kosmos.qsTileFactory.createTile("Test Tile")
+ tileView.init(tile)
+
+ assertThat(tileView.isTileAddedToLongPress).isTrue()
+ assertThat(tileView.isExpandableAddedToLongPress).isTrue()
+ }
+
+ @Test
+ fun onInit_withoutLongPressEffect_longPressEffectDoesNotHaveTileAndExpandable() {
+ tileView = FakeTileView(context, false, null)
+ val tile = kosmos.qsTileFactory.createTile("Test Tile")
+ tileView.init(tile)
+
+ assertThat(tileView.isTileAddedToLongPress).isFalse()
+ assertThat(tileView.isExpandableAddedToLongPress).isFalse()
+ }
+
class FakeTileView(
context: Context,
collapsed: Boolean,
- longPressEffect: QSLongPressEffect?,
- ) : QSTileViewImpl(
+ private val longPressEffect: QSLongPressEffect?,
+ ) :
+ QSTileViewImpl(
ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings),
collapsed,
longPressEffect,
- ) {
+ ) {
var constantLongPressEffectDuration = 500
+ val isTileAddedToLongPress: Boolean
+ get() = longPressEffect?.qsTile != null
+
+ val isExpandableAddedToLongPress: Boolean
+ get() = longPressEffect?.expandable != null
override fun getLongPressEffectDuration(): Int = constantLongPressEffectDuration
+
fun changeState(state: QSTile.State) {
handleStateChanged(state)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
index fc74586..6e6e311 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
@@ -241,7 +241,6 @@
statusBarWinController,
sysUiState,
mock(),
- mock(),
userTracker,
wakefulnessLifecycle,
uiEventLogger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt
index b77a15b..61ea437 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt
@@ -119,4 +119,12 @@
assertThat(lastModel).isEqualTo(isRecording)
}
+
+ @Test
+ fun stopRecording_invokesController() =
+ testScope.runTest {
+ underTest.stopRecording()
+
+ verify(recordingController).stopRecording()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 586adbd..74a2999 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -70,6 +70,7 @@
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.window.StatusBarWindowStateController
+import com.android.systemui.unfold.SysUIUnfoldComponent
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.any
@@ -85,6 +86,7 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Answers
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.anyFloat
@@ -132,6 +134,8 @@
private lateinit var mLockscreenHostedDreamGestureListener: LockscreenHostedDreamGestureListener
@Mock private lateinit var notificationInsetsController: NotificationInsetsController
@Mock private lateinit var mGlanceableHubContainerController: GlanceableHubContainerController
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private lateinit var sysUiUnfoldComponent: SysUIUnfoldComponent
@Mock lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory
@Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent
@Mock lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController
@@ -209,6 +213,7 @@
dozeScrimController,
notificationShadeWindowController,
unfoldTransitionProgressProvider,
+ Optional.of(sysUiUnfoldComponent),
keyguardUnlockAnimationController,
notificationInsetsController,
ambientState,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index e83a46b..31bd12f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -58,6 +58,7 @@
import com.android.systemui.statusbar.phone.DozeServiceHost
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.window.StatusBarWindowStateController
+import com.android.systemui.unfold.SysUIUnfoldComponent
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
@@ -112,6 +113,7 @@
@Mock private lateinit var shadeLogger: ShadeLogger
@Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var pulsingGestureListener: PulsingGestureListener
+ @Mock private lateinit var sysUiUnfoldComponent: SysUIUnfoldComponent
@Mock
private lateinit var mLockscreenHostedDreamGestureListener: LockscreenHostedDreamGestureListener
@Mock private lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory
@@ -181,6 +183,7 @@
dozeScrimController,
notificationShadeWindowController,
unfoldTransitionProgressProvider,
+ Optional.of(sysUiUnfoldComponent),
keyguardUnlockAnimationController,
notificationInsetsController,
ambientState,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt
index a05a23b..293dc04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt
@@ -27,6 +27,9 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito.anyFloat
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@@ -78,6 +81,22 @@
}
@Test
+ fun initMultipleTimes_onTransition_translationIsSetOnlyOnce() {
+ animator.init(parent, MAX_TRANSLATION)
+ animator.init(parent, MAX_TRANSLATION)
+ animator.init(parent, MAX_TRANSLATION)
+
+ // GIVEN one view with a matching id
+ val view = spy(View(context))
+ whenever(parent.findViewById<View>(START_VIEW_ID)).thenReturn(view)
+ progressProvider.onTransitionStarted()
+
+ // WHEN the transition progresses, translation is updated once
+ progressProvider.onTransitionProgress(.5f)
+ verify(view).translationX = anyFloat()
+ }
+
+ @Test
fun onTransition_oneMovesStartWithRTL() {
// GIVEN one view with a matching id
val view = View(context)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractorTest.kt
new file mode 100644
index 0000000..abb6e2b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractorTest.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips.domain.interactor
+
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.domain.interactor.OngoingActivityChipInteractor.Companion.createDialogLaunchOnClickListener
+import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import kotlin.test.Test
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+class OngoingActivityChipInteractorTest : SysuiTestCase() {
+ private val mockSystemUIDialog = mock<SystemUIDialog>()
+ private val dialogDelegate = SystemUIDialog.Delegate { mockSystemUIDialog }
+ private val dialogTransitionAnimator = mock<DialogTransitionAnimator>()
+
+ private val chipBackgroundView = mock<ChipBackgroundContainer>()
+ private val chipView =
+ mock<View>().apply {
+ whenever(
+ this.requireViewById<ChipBackgroundContainer>(
+ R.id.ongoing_activity_chip_background
+ )
+ )
+ .thenReturn(chipBackgroundView)
+ }
+
+ @Test
+ fun createDialogLaunchOnClickListener_showsDialogOnClick() {
+ val clickListener =
+ createDialogLaunchOnClickListener(dialogDelegate, dialogTransitionAnimator)
+
+ // Dialogs must be created on the main thread
+ context.mainExecutor.execute {
+ clickListener.onClick(chipView)
+ verify(dialogTransitionAnimator)
+ .showFromView(
+ eq(mockSystemUIDialog),
+ eq(chipBackgroundView),
+ eq(null),
+ anyBoolean(),
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt
index 0f33b9d..327eec4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt
@@ -16,30 +16,75 @@
package com.android.systemui.statusbar.chips.mediaprojection.domain.interactor
+import android.Manifest
+import android.content.Intent
+import android.content.packageManager
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
+import android.view.View
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.mockDialogTransitionAnimator
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.mediaprojection.data.model.MediaProjectionState
import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
import com.android.systemui.res.R
import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
-import com.android.systemui.statusbar.chips.ui.viewmodel.mediaProjectionChipInteractor
+import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndCastToOtherDeviceDialogDelegate
+import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndShareToAppDialogDelegate
+import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
import com.android.systemui.util.time.fakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mockito.doAnswer
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
@SmallTest
class MediaProjectionChipInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos()
+ private val kosmos = Kosmos().also { it.testCase = this }
private val testScope = kosmos.testScope
private val mediaProjectionRepo = kosmos.fakeMediaProjectionRepository
private val systemClock = kosmos.fakeSystemClock
+ private val mockCastDialog = mock<SystemUIDialog>()
+ private val mockShareDialog = mock<SystemUIDialog>()
+
+ private val chipBackgroundView = mock<ChipBackgroundContainer>()
+ private val chipView =
+ mock<View>().apply {
+ whenever(
+ this.requireViewById<ChipBackgroundContainer>(
+ R.id.ongoing_activity_chip_background
+ )
+ )
+ .thenReturn(chipBackgroundView)
+ }
+
+ @Before
+ fun setUp() {
+ setUpPackageManagerForMediaProjection(kosmos)
+
+ whenever(kosmos.mockSystemUIDialogFactory.create(any<EndCastToOtherDeviceDialogDelegate>()))
+ .thenReturn(mockCastDialog)
+ whenever(kosmos.mockSystemUIDialogFactory.create(any<EndShareToAppDialogDelegate>()))
+ .thenReturn(mockShareDialog)
+ }
+
private val underTest = kosmos.mediaProjectionChipInteractor
@Test
@@ -53,12 +98,15 @@
}
@Test
- fun chip_singleTaskState_isShownWithIcon() =
+ fun chip_singleTaskState_otherDevicesPackage_castToOtherDeviceChipShown() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
mediaProjectionRepo.mediaProjectionState.value =
- MediaProjectionState.SingleTask(createTask(taskId = 1))
+ MediaProjectionState.Projecting.SingleTask(
+ CAST_TO_OTHER_DEVICES_PACKAGE,
+ createTask(taskId = 1)
+ )
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
val icon = (latest as OngoingActivityChipModel.Shown).icon
@@ -66,11 +114,12 @@
}
@Test
- fun chip_entireScreenState_isShownWithIcon() =
+ fun chip_entireScreenState_otherDevicesPackage_castToOtherDeviceChipShown() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
- mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.EntireScreen
+ mediaProjectionRepo.mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(CAST_TO_OTHER_DEVICES_PACKAGE)
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
val icon = (latest as OngoingActivityChipModel.Shown).icon
@@ -78,12 +127,39 @@
}
@Test
+ fun chip_singleTaskState_normalPackage_shareToAppChipShown() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ mediaProjectionRepo.mediaProjectionState.value =
+ MediaProjectionState.Projecting.SingleTask(NORMAL_PACKAGE, createTask(taskId = 1))
+
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+ val icon = (latest as OngoingActivityChipModel.Shown).icon
+ assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenshot_share)
+ }
+
+ @Test
+ fun chip_entireScreenState_normalPackage_shareToAppChipShown() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ mediaProjectionRepo.mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
+
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+ val icon = (latest as OngoingActivityChipModel.Shown).icon
+ assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenshot_share)
+ }
+
+ @Test
fun chip_timeResetsOnEachNewShare() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
systemClock.setElapsedRealtime(1234)
- mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.EntireScreen
+ mediaProjectionRepo.mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(1234)
@@ -92,9 +168,147 @@
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
systemClock.setElapsedRealtime(5678)
- mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.EntireScreen
+ mediaProjectionRepo.mediaProjectionState.value =
+ MediaProjectionState.Projecting.SingleTask(
+ CAST_TO_OTHER_DEVICES_PACKAGE,
+ createTask(taskId = 1)
+ )
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(5678)
}
+
+ @Test
+ fun chip_castToOtherDevice_entireScreen_clickListenerShowsCastDialog() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+ mediaProjectionRepo.mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(CAST_TO_OTHER_DEVICES_PACKAGE)
+
+ val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+
+ // Dialogs must be created on the main thread
+ context.mainExecutor.execute {
+ clickListener.onClick(chipView)
+ verify(kosmos.mockDialogTransitionAnimator)
+ .showFromView(
+ eq(mockCastDialog),
+ eq(chipBackgroundView),
+ eq(null),
+ anyBoolean(),
+ )
+ }
+ }
+
+ @Test
+ fun chip_castToOtherDevice_singleTask_clickListenerShowsCastDialog() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ mediaProjectionRepo.mediaProjectionState.value =
+ MediaProjectionState.Projecting.SingleTask(
+ CAST_TO_OTHER_DEVICES_PACKAGE,
+ createTask(taskId = 1)
+ )
+
+ val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+
+ // Dialogs must be created on the main thread
+ context.mainExecutor.execute {
+ clickListener.onClick(chipView)
+ verify(kosmos.mockDialogTransitionAnimator)
+ .showFromView(
+ eq(mockCastDialog),
+ eq(chipBackgroundView),
+ eq(null),
+ anyBoolean(),
+ )
+ }
+ }
+
+ @Test
+ fun chip_shareToApp_entireScreen_clickListenerShowsShareDialog() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+ mediaProjectionRepo.mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
+
+ val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+
+ // Dialogs must be created on the main thread
+ context.mainExecutor.execute {
+ clickListener.onClick(chipView)
+ verify(kosmos.mockDialogTransitionAnimator)
+ .showFromView(
+ eq(mockShareDialog),
+ eq(chipBackgroundView),
+ eq(null),
+ anyBoolean(),
+ )
+ }
+ }
+
+ @Test
+ fun chip_shareToApp_singleTask_clickListenerShowsShareDialog() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+ mediaProjectionRepo.mediaProjectionState.value =
+ MediaProjectionState.Projecting.SingleTask(NORMAL_PACKAGE, createTask(taskId = 1))
+
+ val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+
+ // Dialogs must be created on the main thread
+ context.mainExecutor.execute {
+ clickListener.onClick(chipView)
+ verify(kosmos.mockDialogTransitionAnimator)
+ .showFromView(
+ eq(mockShareDialog),
+ eq(chipBackgroundView),
+ eq(null),
+ anyBoolean(),
+ )
+ }
+ }
+
+ companion object {
+ const val CAST_TO_OTHER_DEVICES_PACKAGE = "other.devices.package"
+ const val NORMAL_PACKAGE = "some.normal.package"
+
+ /**
+ * Sets up [kosmos.packageManager] so that [CAST_TO_OTHER_DEVICES_PACKAGE] is marked as a
+ * package that casts to other devices, and [NORMAL_PACKAGE] is *not* marked as casting to
+ * other devices.
+ */
+ fun setUpPackageManagerForMediaProjection(kosmos: Kosmos) {
+ kosmos.packageManager.apply {
+ whenever(
+ this.checkPermission(
+ Manifest.permission.REMOTE_DISPLAY_PROVIDER,
+ CAST_TO_OTHER_DEVICES_PACKAGE
+ )
+ )
+ .thenReturn(PackageManager.PERMISSION_GRANTED)
+ whenever(
+ this.checkPermission(
+ Manifest.permission.REMOTE_DISPLAY_PROVIDER,
+ NORMAL_PACKAGE
+ )
+ )
+ .thenReturn(PackageManager.PERMISSION_DENIED)
+
+ doAnswer {
+ // See Utils.isHeadlessRemoteDisplayProvider
+ if (
+ (it.arguments[0] as Intent).`package` == CAST_TO_OTHER_DEVICES_PACKAGE
+ ) {
+ emptyList()
+ } else {
+ listOf(mock<ResolveInfo>())
+ }
+ }
+ .whenever(this)
+ .queryIntentActivities(any(), anyInt())
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegateTest.kt
new file mode 100644
index 0000000..7b676e2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegateTest.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.statusbar.chips.mediaprojection.ui.view
+
+import android.content.ComponentName
+import android.content.DialogInterface
+import android.content.Intent
+import android.content.packageManager
+import android.content.pm.ApplicationInfo
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
+import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.mediaProjectionChipInteractor
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.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
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+class EndCastToOtherDeviceDialogDelegateTest : SysuiTestCase() {
+ private val kosmos = Kosmos().also { it.testCase = this }
+ private val sysuiDialog = mock<SystemUIDialog>()
+ private lateinit var underTest: EndCastToOtherDeviceDialogDelegate
+
+ @Test
+ fun icon() {
+ createAndSetDelegate(ENTIRE_SCREEN)
+
+ underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+ verify(sysuiDialog).setIcon(R.drawable.ic_cast_connected)
+ }
+
+ @Test
+ fun title() {
+ createAndSetDelegate(SINGLE_TASK)
+
+ underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+ verify(sysuiDialog).setTitle(R.string.cast_to_other_device_stop_dialog_title)
+ }
+
+ @Test
+ fun message_entireScreen() {
+ createAndSetDelegate(ENTIRE_SCREEN)
+
+ underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+ verify(sysuiDialog)
+ .setMessage(context.getString(R.string.cast_to_other_device_stop_dialog_message))
+ }
+
+ @Test
+ fun message_singleTask() {
+ val baseIntent =
+ Intent().apply { this.component = ComponentName("fake.task.package", "cls") }
+ val appInfo = mock<ApplicationInfo>()
+ whenever(appInfo.loadLabel(kosmos.packageManager)).thenReturn("Fake Package")
+ whenever(kosmos.packageManager.getApplicationInfo(eq("fake.task.package"), any<Int>()))
+ .thenReturn(appInfo)
+
+ createAndSetDelegate(
+ MediaProjectionState.Projecting.SingleTask(
+ HOST_PACKAGE,
+ createTask(taskId = 1, baseIntent = baseIntent)
+ )
+ )
+
+ underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+ // It'd be nice to use R.string.cast_to_other_device_stop_dialog_message_specific_app
+ // directly, but it includes the <b> tags which aren't in the returned string.
+ val result = argumentCaptor<CharSequence>()
+ verify(sysuiDialog).setMessage(result.capture())
+ assertThat(result.firstValue.toString()).isEqualTo("You will stop casting Fake Package")
+ }
+
+ @Test
+ fun negativeButton() {
+ createAndSetDelegate(ENTIRE_SCREEN)
+
+ underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+ verify(sysuiDialog).setNegativeButton(R.string.close_dialog_button, null)
+ }
+
+ @Test
+ fun positiveButton() =
+ kosmos.testScope.runTest {
+ createAndSetDelegate(SINGLE_TASK)
+
+ underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+ val clickListener = argumentCaptor<DialogInterface.OnClickListener>()
+
+ // Verify the button has the right text
+ verify(sysuiDialog)
+ .setPositiveButton(
+ eq(R.string.cast_to_other_device_stop_dialog_button),
+ clickListener.capture()
+ )
+
+ // Verify that clicking the button stops the recording
+ assertThat(kosmos.fakeMediaProjectionRepository.stopProjectingInvoked).isFalse()
+
+ clickListener.firstValue.onClick(mock<DialogInterface>(), 0)
+ runCurrent()
+
+ assertThat(kosmos.fakeMediaProjectionRepository.stopProjectingInvoked).isTrue()
+ }
+
+ private fun createAndSetDelegate(state: MediaProjectionState.Projecting) {
+ underTest =
+ EndCastToOtherDeviceDialogDelegate(
+ kosmos.endMediaProjectionDialogHelper,
+ kosmos.mediaProjectionChipInteractor,
+ state,
+ )
+ }
+
+ companion object {
+ private const val HOST_PACKAGE = "fake.host.package"
+ private val ENTIRE_SCREEN = MediaProjectionState.Projecting.EntireScreen(HOST_PACKAGE)
+ private val SINGLE_TASK =
+ MediaProjectionState.Projecting.SingleTask(HOST_PACKAGE, createTask(taskId = 1))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt
new file mode 100644
index 0000000..bbd1109
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips.mediaprojection.ui.view
+
+import android.content.ComponentName
+import android.content.Intent
+import android.content.packageManager
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+class EndMediaProjectionDialogHelperTest : SysuiTestCase() {
+ private val kosmos = Kosmos().also { it.testCase = this }
+
+ private val underTest = kosmos.endMediaProjectionDialogHelper
+
+ @Test
+ fun createDialog_usesDelegateAndFactory() {
+ val dialog = mock<SystemUIDialog>()
+ val delegate = SystemUIDialog.Delegate { dialog }
+ whenever(kosmos.mockSystemUIDialogFactory.create(eq(delegate))).thenReturn(dialog)
+
+ underTest.createDialog(delegate)
+
+ verify(kosmos.mockSystemUIDialogFactory).create(delegate)
+ }
+
+ @Test
+ fun getDialogMessage_entireScreen_isGenericMessage() {
+ val result =
+ underTest.getDialogMessage(
+ MediaProjectionState.Projecting.EntireScreen("host.package"),
+ R.string.accessibility_home,
+ R.string.cast_to_other_device_stop_dialog_message_specific_app
+ )
+
+ assertThat(result).isEqualTo(context.getString(R.string.accessibility_home))
+ }
+
+ @Test
+ fun getDialogMessage_singleTask_cannotFindPackage_isGenericMessage() {
+ val baseIntent =
+ Intent().apply { this.component = ComponentName("fake.task.package", "cls") }
+ whenever(kosmos.packageManager.getApplicationInfo(eq("fake.task.package"), any<Int>()))
+ .thenThrow(PackageManager.NameNotFoundException())
+
+ val projectionState =
+ MediaProjectionState.Projecting.SingleTask(
+ "host.package",
+ createTask(taskId = 1, baseIntent = baseIntent)
+ )
+
+ val result =
+ underTest.getDialogMessage(
+ projectionState,
+ R.string.accessibility_home,
+ R.string.cast_to_other_device_stop_dialog_message_specific_app
+ )
+
+ assertThat(result).isEqualTo(context.getString(R.string.accessibility_home))
+ }
+
+ @Test
+ fun getDialogMessage_singleTask_findsPackage_isSpecificMessageWithAppLabel() {
+ val baseIntent =
+ Intent().apply { this.component = ComponentName("fake.task.package", "cls") }
+ val appInfo = mock<ApplicationInfo>()
+ whenever(appInfo.loadLabel(kosmos.packageManager)).thenReturn("Fake Package")
+ whenever(kosmos.packageManager.getApplicationInfo(eq("fake.task.package"), any<Int>()))
+ .thenReturn(appInfo)
+
+ val projectionState =
+ MediaProjectionState.Projecting.SingleTask(
+ "host.package",
+ createTask(taskId = 1, baseIntent = baseIntent)
+ )
+
+ val result =
+ underTest.getDialogMessage(
+ projectionState,
+ R.string.accessibility_home,
+ R.string.cast_to_other_device_stop_dialog_message_specific_app
+ )
+
+ // It'd be nice to use the R.string resources directly, but they include the <b> tags which
+ // aren't in the returned string.
+ assertThat(result.toString()).isEqualTo("You will stop casting Fake Package")
+ }
+
+ @Test
+ fun getDialogMessage_appLabelHasSpecialCharacters_isEscaped() {
+ val baseIntent =
+ Intent().apply { this.component = ComponentName("fake.task.package", "cls") }
+ val appInfo = mock<ApplicationInfo>()
+ whenever(appInfo.loadLabel(kosmos.packageManager)).thenReturn("Fake & Package <Here>")
+ whenever(kosmos.packageManager.getApplicationInfo(eq("fake.task.package"), any<Int>()))
+ .thenReturn(appInfo)
+
+ val projectionState =
+ MediaProjectionState.Projecting.SingleTask(
+ "host.package",
+ createTask(taskId = 1, baseIntent = baseIntent)
+ )
+
+ val result =
+ underTest.getDialogMessage(
+ projectionState,
+ R.string.accessibility_home,
+ R.string.cast_to_other_device_stop_dialog_message_specific_app
+ )
+
+ assertThat(result.toString()).isEqualTo("You will stop casting Fake & Package <Here>")
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegateTest.kt
new file mode 100644
index 0000000..4ddca52
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegateTest.kt
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips.mediaprojection.ui.view
+
+import android.content.ComponentName
+import android.content.DialogInterface
+import android.content.Intent
+import android.content.packageManager
+import android.content.pm.ApplicationInfo
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
+import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.mediaProjectionChipInteractor
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.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
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+class EndShareToAppDialogDelegateTest : SysuiTestCase() {
+ private val kosmos = Kosmos().also { it.testCase = this }
+ private val sysuiDialog = mock<SystemUIDialog>()
+ private lateinit var underTest: EndShareToAppDialogDelegate
+
+ @Test
+ fun icon() {
+ createAndSetDelegate(SINGLE_TASK)
+
+ underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+ verify(sysuiDialog).setIcon(R.drawable.ic_screenshot_share)
+ }
+
+ @Test
+ fun title() {
+ createAndSetDelegate(ENTIRE_SCREEN)
+
+ underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+ verify(sysuiDialog).setTitle(R.string.share_to_app_stop_dialog_title)
+ }
+
+ @Test
+ fun message_entireScreen() {
+ createAndSetDelegate(ENTIRE_SCREEN)
+
+ underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+ verify(sysuiDialog).setMessage(context.getString(R.string.share_to_app_stop_dialog_message))
+ }
+
+ @Test
+ fun message_singleTask() {
+ val baseIntent =
+ Intent().apply { this.component = ComponentName("fake.task.package", "cls") }
+ val appInfo = mock<ApplicationInfo>()
+ whenever(appInfo.loadLabel(kosmos.packageManager)).thenReturn("Fake Package")
+ whenever(kosmos.packageManager.getApplicationInfo(eq("fake.task.package"), any<Int>()))
+ .thenReturn(appInfo)
+
+ createAndSetDelegate(
+ MediaProjectionState.Projecting.SingleTask(
+ HOST_PACKAGE,
+ createTask(taskId = 1, baseIntent = baseIntent)
+ )
+ )
+
+ underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+ // It'd be nice to use R.string.share_to_app_stop_dialog_message_specific_app directly, but
+ // it includes the <b> tags which aren't in the returned string.
+ val result = argumentCaptor<CharSequence>()
+ verify(sysuiDialog).setMessage(result.capture())
+ assertThat(result.firstValue.toString()).isEqualTo("You will stop sharing Fake Package")
+ }
+
+ @Test
+ fun negativeButton() {
+ createAndSetDelegate(SINGLE_TASK)
+
+ underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+ verify(sysuiDialog).setNegativeButton(R.string.close_dialog_button, null)
+ }
+
+ @Test
+ fun positiveButton() =
+ kosmos.testScope.runTest {
+ createAndSetDelegate(ENTIRE_SCREEN)
+
+ underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+ val clickListener = argumentCaptor<DialogInterface.OnClickListener>()
+
+ // Verify the button has the right text
+ verify(sysuiDialog)
+ .setPositiveButton(
+ eq(R.string.share_to_app_stop_dialog_button),
+ clickListener.capture()
+ )
+
+ // Verify that clicking the button stops the recording
+ assertThat(kosmos.fakeMediaProjectionRepository.stopProjectingInvoked).isFalse()
+
+ clickListener.firstValue.onClick(mock<DialogInterface>(), 0)
+ runCurrent()
+
+ assertThat(kosmos.fakeMediaProjectionRepository.stopProjectingInvoked).isTrue()
+ }
+
+ private fun createAndSetDelegate(state: MediaProjectionState.Projecting) {
+ underTest =
+ EndShareToAppDialogDelegate(
+ kosmos.endMediaProjectionDialogHelper,
+ kosmos.mediaProjectionChipInteractor,
+ state,
+ )
+ }
+
+ companion object {
+ private const val HOST_PACKAGE = "fake.host.package"
+ private val ENTIRE_SCREEN = MediaProjectionState.Projecting.EntireScreen(HOST_PACKAGE)
+ private val SINGLE_TASK =
+ MediaProjectionState.Projecting.SingleTask(HOST_PACKAGE, createTask(taskId = 1))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt
index 25efaf1..f6c3adb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt
@@ -16,8 +16,10 @@
package com.android.systemui.statusbar.chips.screenrecord.domain.interactor
+import android.view.View
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.mockDialogTransitionAnimator
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.Kosmos
@@ -26,11 +28,21 @@
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
import com.android.systemui.screenrecord.data.repository.screenRecordRepository
import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
import com.android.systemui.statusbar.chips.ui.viewmodel.screenRecordChipInteractor
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
import com.android.systemui.util.time.fakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
@SmallTest
class ScreenRecordChipInteractorTest : SysuiTestCase() {
@@ -38,9 +50,27 @@
private val testScope = kosmos.testScope
private val screenRecordRepo = kosmos.screenRecordRepository
private val systemClock = kosmos.fakeSystemClock
+ private val mockSystemUIDialog = mock<SystemUIDialog>()
+
+ private val chipBackgroundView = mock<ChipBackgroundContainer>()
+ private val chipView =
+ mock<View>().apply {
+ whenever(
+ this.requireViewById<ChipBackgroundContainer>(
+ R.id.ongoing_activity_chip_background
+ )
+ )
+ .thenReturn(chipBackgroundView)
+ }
private val underTest = kosmos.screenRecordChipInteractor
+ @Before
+ fun setUp() {
+ whenever(kosmos.mockSystemUIDialogFactory.create(any<SystemUIDialog.Delegate>()))
+ .thenReturn(mockSystemUIDialog)
+ }
+
@Test
fun chip_doingNothingState_isHidden() =
testScope.runTest {
@@ -70,7 +100,7 @@
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
val icon = (latest as OngoingActivityChipModel.Shown).icon
- assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.stat_sys_screen_record)
+ assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenrecord)
}
@Test
@@ -93,4 +123,25 @@
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(5678)
}
+
+ @Test
+ fun chip_clickListenerShowsDialog() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+ screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
+
+ val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+
+ // Dialogs must be created on the main thread
+ context.mainExecutor.execute {
+ clickListener.onClick(chipView)
+ verify(kosmos.mockDialogTransitionAnimator)
+ .showFromView(
+ eq(mockSystemUIDialog),
+ eq(chipBackgroundView),
+ eq(null),
+ anyBoolean(),
+ )
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt
new file mode 100644
index 0000000..bca6763
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips.screenrecord.ui.view
+
+import android.content.DialogInterface
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.res.R
+import com.android.systemui.screenrecord.data.repository.screenRecordRepository
+import com.android.systemui.statusbar.chips.ui.viewmodel.screenRecordChipInteractor
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+class EndScreenRecordingDialogDelegateTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+
+ private val sysuiDialog = mock<SystemUIDialog>()
+ private val sysuiDialogFactory = kosmos.mockSystemUIDialogFactory
+
+ private val underTest =
+ EndScreenRecordingDialogDelegate(
+ sysuiDialogFactory,
+ kosmos.screenRecordChipInteractor,
+ )
+
+ @Before
+ fun setUp() {
+ whenever(sysuiDialogFactory.create(eq(underTest), eq(context))).thenReturn(sysuiDialog)
+ }
+
+ @Test
+ fun icon() {
+ underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+ verify(sysuiDialog).setIcon(R.drawable.ic_screenrecord)
+ }
+
+ @Test
+ fun title() {
+ underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+ verify(sysuiDialog).setTitle(R.string.screenrecord_stop_dialog_title)
+ }
+
+ @Test
+ fun message() {
+ underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+ verify(sysuiDialog).setMessage(R.string.screenrecord_stop_dialog_message)
+ }
+
+ @Test
+ fun negativeButton() {
+ underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+ verify(sysuiDialog).setNegativeButton(R.string.close_dialog_button, null)
+ }
+
+ @Test
+ fun positiveButton() =
+ kosmos.testScope.runTest {
+ underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+ val clickListener = argumentCaptor<DialogInterface.OnClickListener>()
+
+ // Verify the button has the right text
+ verify(sysuiDialog)
+ .setPositiveButton(
+ eq(R.string.screenrecord_stop_dialog_button),
+ clickListener.capture()
+ )
+
+ // Verify that clicking the button stops the recording
+ assertThat(kosmos.screenRecordRepository.stopRecordingInvoked).isFalse()
+
+ clickListener.firstValue.onClick(mock<DialogInterface>(), 0)
+ runCurrent()
+
+ assertThat(kosmos.screenRecordRepository.stopRecordingInvoked).isTrue()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
index 121229c..65bf0bc 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
@@ -22,6 +22,7 @@
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.mediaprojection.data.model.MediaProjectionState
import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
@@ -30,14 +31,16 @@
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
import com.android.systemui.screenrecord.data.repository.screenRecordRepository
import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE
+import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
+import org.junit.Before
import org.junit.Test
@SmallTest
class OngoingActivityChipsViewModelTest : SysuiTestCase() {
-
- private val kosmos = Kosmos()
+ private val kosmos = Kosmos().also { it.testCase = this }
private val testScope = kosmos.testScope
private val screenRecordState = kosmos.screenRecordRepository.screenRecordState
@@ -46,6 +49,11 @@
private val underTest = kosmos.ongoingActivityChipsViewModel
+ @Before
+ fun setUp() {
+ setUpPackageManagerForMediaProjection(kosmos)
+ }
+
@Test
fun chip_allHidden_hidden() =
testScope.runTest {
@@ -91,7 +99,8 @@
fun chip_screenRecordShowAndMediaProjectionShow_screenRecordShown() =
testScope.runTest {
screenRecordState.value = ScreenRecordModel.Recording
- mediaProjectionState.value = MediaProjectionState.EntireScreen
+ mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
callState.value = OngoingActivityChipModel.Hidden
val latest by collectLastValue(underTest.chip)
@@ -103,7 +112,8 @@
fun chip_mediaProjectionShowAndCallShow_mediaProjectionShown() =
testScope.runTest {
screenRecordState.value = ScreenRecordModel.DoingNothing
- mediaProjectionState.value = MediaProjectionState.EntireScreen
+ mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
val callChip =
OngoingActivityChipModel.Shown(
Icon.Resource(R.drawable.ic_call, ContentDescription.Loaded("icon")),
@@ -113,7 +123,7 @@
val latest by collectLastValue(underTest.chip)
- assertIsMediaProjectionChip(latest)
+ assertIsShareToAppChip(latest)
}
@Test
@@ -152,10 +162,14 @@
assertThat(latest).isEqualTo(callChip)
// WHEN the higher priority media projection chip is added
- mediaProjectionState.value = MediaProjectionState.SingleTask(createTask(taskId = 1))
+ mediaProjectionState.value =
+ MediaProjectionState.Projecting.SingleTask(
+ NORMAL_PACKAGE,
+ createTask(taskId = 1),
+ )
// THEN the higher priority media projection chip is used
- assertIsMediaProjectionChip(latest)
+ assertIsShareToAppChip(latest)
// WHEN the higher priority screen record chip is added
screenRecordState.value = ScreenRecordModel.Recording
@@ -169,7 +183,8 @@
testScope.runTest {
// WHEN all chips are active
screenRecordState.value = ScreenRecordModel.Recording
- mediaProjectionState.value = MediaProjectionState.EntireScreen
+ mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
val callChip =
OngoingActivityChipModel.Shown(
@@ -187,7 +202,7 @@
screenRecordState.value = ScreenRecordModel.DoingNothing
// THEN the lower priority media projection is used
- assertIsMediaProjectionChip(latest)
+ assertIsShareToAppChip(latest)
// WHEN the higher priority media projection is removed
mediaProjectionState.value = MediaProjectionState.NotProjecting
@@ -200,13 +215,13 @@
fun assertIsScreenRecordChip(latest: OngoingActivityChipModel?) {
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
val icon = (latest as OngoingActivityChipModel.Shown).icon
- assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.stat_sys_screen_record)
+ assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenrecord)
}
- fun assertIsMediaProjectionChip(latest: OngoingActivityChipModel?) {
+ fun assertIsShareToAppChip(latest: OngoingActivityChipModel?) {
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
val icon = (latest as OngoingActivityChipModel.Shown).icon
- assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_cast_connected)
+ assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenshot_share)
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
index 057dcb2..6af14e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
@@ -35,6 +35,7 @@
import com.android.systemui.statusbar.phone.LetterboxAppearanceCalculator
import com.android.systemui.statusbar.phone.StatusBarBoundsProvider
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
+import com.android.systemui.statusbar.phone.ongoingcall.data.model.OngoingCallModel
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
@@ -390,7 +391,7 @@
testScope.runTest {
val latest by collectLastValue(underTest.statusBarAppearance)
- ongoingCallRepository.setHasOngoingCall(true)
+ ongoingCallRepository.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 34))
onSystemBarAttributesChanged(
requestedVisibleTypes = WindowInsets.Type.navigationBars(),
)
@@ -403,7 +404,7 @@
testScope.runTest {
val latest by collectLastValue(underTest.statusBarAppearance)
- ongoingCallRepository.setHasOngoingCall(true)
+ ongoingCallRepository.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 789))
onSystemBarAttributesChanged(
requestedVisibleTypes = WindowInsets.Type.statusBars(),
appearance = APPEARANCE_OPAQUE_STATUS_BARS,
@@ -417,7 +418,7 @@
testScope.runTest {
val latest by collectLastValue(underTest.statusBarAppearance)
- ongoingCallRepository.setHasOngoingCall(false)
+ ongoingCallRepository.setOngoingCallState(OngoingCallModel.NoCall)
onSystemBarAttributesChanged(
requestedVisibleTypes = WindowInsets.Type.navigationBars(),
appearance = APPEARANCE_OPAQUE_STATUS_BARS,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
index 068e166..066ca1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -762,6 +762,7 @@
// THEN the existing session is reused and views are registered
verify(smartspaceManager, never()).createSmartspaceSession(any())
verify(smartspaceView2).setUiSurface(BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
+ verify(smartspaceView2).setTimeChangedDelegate(any())
verify(smartspaceView2).registerDataProvider(plugin)
verify(smartspaceView2).registerConfigProvider(configPlugin)
}
@@ -836,6 +837,7 @@
verify(dateSmartspaceView).setUiSurface(
BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
+ verify(dateSmartspaceView).setTimeChangedDelegate(any())
verify(dateSmartspaceView).registerDataProvider(datePlugin)
verify(dateSmartspaceView).setPrimaryTextColor(anyInt())
@@ -848,6 +850,7 @@
verify(weatherSmartspaceView).setUiSurface(
BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
+ verify(weatherSmartspaceView).setTimeChangedDelegate(any())
verify(weatherSmartspaceView).registerDataProvider(weatherPlugin)
verify(weatherSmartspaceView).setPrimaryTextColor(anyInt())
@@ -859,6 +862,7 @@
controller.stateChangeListener.onViewAttachedToWindow(view)
verify(smartspaceView).setUiSurface(BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
+ verify(smartspaceView).setTimeChangedDelegate(any())
verify(smartspaceView).registerDataProvider(plugin)
verify(smartspaceView).registerConfigProvider(configPlugin)
verify(smartspaceSession)
@@ -984,6 +988,10 @@
override fun setUiSurface(uiSurface: String) {
}
+ override fun setTimeChangedDelegate(
+ delegate: BcSmartspaceDataPlugin.TimeChangedDelegate?
+ ) {}
+
override fun setDozeAmount(amount: Float) {
}
@@ -1012,6 +1020,10 @@
override fun setUiSurface(uiSurface: String) {
}
+ override fun setTimeChangedDelegate(
+ delegate: BcSmartspaceDataPlugin.TimeChangedDelegate?
+ ) {}
+
override fun setDozeAmount(amount: Float) {
}
@@ -1036,6 +1048,10 @@
override fun setUiSurface(uiSurface: String) {
}
+ override fun setTimeChangedDelegate(
+ delegate: BcSmartspaceDataPlugin.TimeChangedDelegate?
+ ) {}
+
override fun setDozeAmount(amount: Float) {
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
index c9f2add..26f5370 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
@@ -39,6 +39,7 @@
import com.android.systemui.statusbar.notification.shared.byIsSilent
import com.android.systemui.statusbar.notification.shared.byIsSuppressedFromStatusBar
import com.android.systemui.statusbar.notification.shared.byKey
+import com.android.systemui.user.domain.UserDomainLayerModule
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -156,7 +157,14 @@
private val bubbles: Bubbles = mock()
- @Component(modules = [SysUITestModule::class, BiometricsDomainLayerModule::class])
+ @Component(
+ modules =
+ [
+ SysUITestModule::class,
+ BiometricsDomainLayerModule::class,
+ UserDomainLayerModule::class,
+ ]
+ )
@SysUISingleton
interface TestComponent : SysUITestComponent<AlwaysOnDisplayNotificationIconsInteractor> {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index f3d6407..53e643e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -223,7 +223,8 @@
mFakeExecutor,
mBackgroundExecutor,
mLogger,
- mStatusOverlayHoverListenerFactory
+ mStatusOverlayHoverListenerFactory,
+ mKosmos.getCommunalSceneInteractor()
);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index 4d6798b..feef943 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -32,16 +32,17 @@
import android.widget.LinearLayout
import androidx.test.filters.SmallTest
import com.android.internal.logging.testing.UiEventLoggerFake
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository
import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
-import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository
+import com.android.systemui.statusbar.phone.ongoingcall.data.model.OngoingCallModel
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.util.concurrency.FakeExecutor
@@ -60,13 +61,13 @@
import org.mockito.ArgumentMatchers.anyString
import org.mockito.ArgumentMatchers.nullable
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.eq
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
private const val CALL_UID = 900
@@ -93,8 +94,8 @@
private lateinit var controller: OngoingCallController
private lateinit var notifCollectionListener: NotifCollectionListener
- @Mock private lateinit var mockSwipeStatusBarAwayGestureHandler:
- SwipeStatusBarAwayGestureHandler
+ @Mock
+ private lateinit var mockSwipeStatusBarAwayGestureHandler: SwipeStatusBarAwayGestureHandler
@Mock private lateinit var mockOngoingCallListener: OngoingCallListener
@Mock private lateinit var mockActivityStarter: ActivityStarter
@Mock private lateinit var mockIActivityManager: IActivityManager
@@ -112,21 +113,22 @@
MockitoAnnotations.initMocks(this)
val notificationCollection = mock(CommonNotifCollection::class.java)
- controller = OngoingCallController(
- testScope.backgroundScope,
- context,
- ongoingCallRepository,
- notificationCollection,
- clock,
- mockActivityStarter,
- mainExecutor,
- mockIActivityManager,
- OngoingCallLogger(uiEventLoggerFake),
- DumpManager(),
- mockStatusBarWindowController,
- mockSwipeStatusBarAwayGestureHandler,
- statusBarModeRepository,
- )
+ controller =
+ OngoingCallController(
+ testScope.backgroundScope,
+ context,
+ ongoingCallRepository,
+ notificationCollection,
+ clock,
+ mockActivityStarter,
+ mainExecutor,
+ mockIActivityManager,
+ OngoingCallLogger(uiEventLoggerFake),
+ DumpManager(),
+ mockStatusBarWindowController,
+ mockSwipeStatusBarAwayGestureHandler,
+ statusBarModeRepository,
+ )
controller.start()
controller.addCallback(mockOngoingCallListener)
controller.setChipView(chipView)
@@ -136,7 +138,7 @@
notifCollectionListener = collectionListenerCaptor.value!!
`when`(mockIActivityManager.getUidProcessState(eq(CALL_UID), nullable(String::class.java)))
- .thenReturn(PROC_STATE_INVISIBLE)
+ .thenReturn(PROC_STATE_INVISIBLE)
}
@After
@@ -146,10 +148,14 @@
@Test
fun onEntryUpdated_isOngoingCallNotif_listenerAndRepoNotified() {
- notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+ val notification = NotificationEntryBuilder(createOngoingCallNotifEntry())
+ notification.modifyNotification(context).setWhen(567)
+ notifCollectionListener.onEntryUpdated(notification.build())
verify(mockOngoingCallListener).onOngoingCallStateChanged(anyBoolean())
- assertThat(ongoingCallRepository.hasOngoingCall.value).isTrue()
+ val repoState = ongoingCallRepository.ongoingCallState.value
+ assertThat(repoState).isInstanceOf(OngoingCallModel.InCall::class.java)
+ assertThat((repoState as OngoingCallModel.InCall).startTimeMs).isEqualTo(567)
}
@Test
@@ -164,7 +170,8 @@
notifCollectionListener.onEntryUpdated(createNotCallNotifEntry())
verify(mockOngoingCallListener, never()).onOngoingCallStateChanged(anyBoolean())
- assertThat(ongoingCallRepository.hasOngoingCall.value).isFalse()
+ assertThat(ongoingCallRepository.ongoingCallState.value)
+ .isInstanceOf(OngoingCallModel.NoCall::class.java)
}
@Test
@@ -172,25 +179,27 @@
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
notifCollectionListener.onEntryUpdated(createScreeningCallNotifEntry())
- verify(mockOngoingCallListener, times(2))
- .onOngoingCallStateChanged(anyBoolean())
+ verify(mockOngoingCallListener, times(2)).onOngoingCallStateChanged(anyBoolean())
}
@Test
fun onEntryUpdated_ongoingCallNotifThenScreeningCallNotif_repoUpdated() {
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
- assertThat(ongoingCallRepository.hasOngoingCall.value).isTrue()
+ assertThat(ongoingCallRepository.ongoingCallState.value)
+ .isInstanceOf(OngoingCallModel.InCall::class.java)
notifCollectionListener.onEntryUpdated(createScreeningCallNotifEntry())
- assertThat(ongoingCallRepository.hasOngoingCall.value).isFalse()
+ assertThat(ongoingCallRepository.ongoingCallState.value)
+ .isInstanceOf(OngoingCallModel.NoCall::class.java)
}
/** Regression test for b/191472854. */
@Test
fun onEntryUpdated_notifHasNullContentIntent_noCrash() {
notifCollectionListener.onEntryUpdated(
- createCallNotifEntry(ongoingCallStyle, nullContentIntent = true))
+ createCallNotifEntry(ongoingCallStyle, nullContentIntent = true)
+ )
}
/** Regression test for b/192379214. */
@@ -202,12 +211,12 @@
notifCollectionListener.onEntryUpdated(notification.build())
chipView.measure(
- View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
- View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
)
assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth)
- .isEqualTo(0)
+ .isEqualTo(0)
}
@Test
@@ -218,12 +227,12 @@
notifCollectionListener.onEntryUpdated(notification.build())
chipView.measure(
- View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
- View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
)
assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth)
- .isGreaterThan(0)
+ .isGreaterThan(0)
}
@Test
@@ -233,12 +242,12 @@
notifCollectionListener.onEntryUpdated(notification.build())
chipView.measure(
- View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
- View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
)
assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth)
- .isGreaterThan(0)
+ .isGreaterThan(0)
}
/** Regression test for b/194731244. */
@@ -250,15 +259,14 @@
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
}
- verify(mockIActivityManager, times(1))
- .registerUidObserver(any(), any(), any(), any())
+ verify(mockIActivityManager, times(1)).registerUidObserver(any(), any(), any(), any())
}
/** Regression test for b/216248574. */
@Test
fun entryUpdated_getUidProcessStateThrowsException_noCrash() {
`when`(mockIActivityManager.getUidProcessState(eq(CALL_UID), nullable(String::class.java)))
- .thenThrow(SecurityException())
+ .thenThrow(SecurityException())
// No assert required, just check no crash
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
@@ -267,9 +275,15 @@
/** Regression test for b/216248574. */
@Test
fun entryUpdated_registerUidObserverThrowsException_noCrash() {
- `when`(mockIActivityManager.registerUidObserver(
- any(), any(), any(), nullable(String::class.java)
- )).thenThrow(SecurityException())
+ `when`(
+ mockIActivityManager.registerUidObserver(
+ any(),
+ any(),
+ any(),
+ nullable(String::class.java),
+ )
+ )
+ .thenThrow(SecurityException())
// No assert required, just check no crash
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
@@ -281,9 +295,8 @@
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
val packageNameCaptor = ArgumentCaptor.forClass(String::class.java)
- verify(mockIActivityManager).registerUidObserver(
- any(), any(), any(), packageNameCaptor.capture()
- )
+ verify(mockIActivityManager)
+ .registerUidObserver(any(), any(), any(), packageNameCaptor.capture())
assertThat(packageNameCaptor.value).isNotNull()
}
@@ -313,11 +326,13 @@
fun onEntryRemoved_callNotifAddedThenRemoved_repoUpdated() {
val ongoingCallNotifEntry = createOngoingCallNotifEntry()
notifCollectionListener.onEntryAdded(ongoingCallNotifEntry)
- assertThat(ongoingCallRepository.hasOngoingCall.value).isTrue()
+ assertThat(ongoingCallRepository.ongoingCallState.value)
+ .isInstanceOf(OngoingCallModel.InCall::class.java)
notifCollectionListener.onEntryRemoved(ongoingCallNotifEntry, REASON_USER_STOPPED)
- assertThat(ongoingCallRepository.hasOngoingCall.value).isFalse()
+ assertThat(ongoingCallRepository.ongoingCallState.value)
+ .isInstanceOf(OngoingCallModel.NoCall::class.java)
}
@Test
@@ -360,7 +375,8 @@
notifCollectionListener.onEntryRemoved(removedEntryBuilder.build(), REASON_USER_STOPPED)
- assertThat(ongoingCallRepository.hasOngoingCall.value).isFalse()
+ assertThat(ongoingCallRepository.ongoingCallState.value)
+ .isInstanceOf(OngoingCallModel.NoCall::class.java)
}
@Test
@@ -379,7 +395,8 @@
notifCollectionListener.onEntryRemoved(createNotCallNotifEntry(), REASON_USER_STOPPED)
- assertThat(ongoingCallRepository.hasOngoingCall.value).isTrue()
+ assertThat(ongoingCallRepository.ongoingCallState.value)
+ .isInstanceOf(OngoingCallModel.InCall::class.java)
}
@Test
@@ -404,7 +421,7 @@
@Test
fun hasOngoingCall_ongoingCallNotifSentAndCallAppNotVisible_returnsTrue() {
`when`(mockIActivityManager.getUidProcessState(eq(CALL_UID), nullable(String::class.java)))
- .thenReturn(PROC_STATE_INVISIBLE)
+ .thenReturn(PROC_STATE_INVISIBLE)
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
@@ -414,7 +431,7 @@
@Test
fun hasOngoingCall_ongoingCallNotifSentButCallAppVisible_returnsFalse() {
`when`(mockIActivityManager.getUidProcessState(eq(CALL_UID), nullable(String::class.java)))
- .thenReturn(PROC_STATE_VISIBLE)
+ .thenReturn(PROC_STATE_VISIBLE)
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
@@ -472,10 +489,8 @@
lateinit var newChipView: View
TestableLooper.get(this).runWithLooper {
- newChipView = LayoutInflater.from(mContext).inflate(
- R.layout.ongoing_activity_chip,
- null
- )
+ newChipView =
+ LayoutInflater.from(mContext).inflate(R.layout.ongoing_activity_chip, null)
}
// Change the chip view associated with the controller.
@@ -488,13 +503,13 @@
fun callProcessChangesToVisible_listenerNotified() {
// Start the call while the process is invisible.
`when`(mockIActivityManager.getUidProcessState(eq(CALL_UID), nullable(String::class.java)))
- .thenReturn(PROC_STATE_INVISIBLE)
+ .thenReturn(PROC_STATE_INVISIBLE)
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
reset(mockOngoingCallListener)
val captor = ArgumentCaptor.forClass(IUidObserver.Stub::class.java)
- verify(mockIActivityManager).registerUidObserver(
- captor.capture(), any(), any(), nullable(String::class.java))
+ verify(mockIActivityManager)
+ .registerUidObserver(captor.capture(), any(), any(), nullable(String::class.java))
val uidObserver = captor.value
// Update the process to visible.
@@ -509,13 +524,13 @@
fun callProcessChangesToInvisible_listenerNotified() {
// Start the call while the process is visible.
`when`(mockIActivityManager.getUidProcessState(eq(CALL_UID), nullable(String::class.java)))
- .thenReturn(PROC_STATE_VISIBLE)
+ .thenReturn(PROC_STATE_VISIBLE)
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
reset(mockOngoingCallListener)
val captor = ArgumentCaptor.forClass(IUidObserver.Stub::class.java)
- verify(mockIActivityManager).registerUidObserver(
- captor.capture(), any(), any(), nullable(String::class.java))
+ verify(mockIActivityManager)
+ .registerUidObserver(captor.capture(), any(), any(), nullable(String::class.java))
val uidObserver = captor.value
// Update the process to invisible.
@@ -534,7 +549,7 @@
assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
assertThat(uiEventLoggerFake.eventId(0))
- .isEqualTo(OngoingCallLogger.OngoingCallEvents.ONGOING_CALL_CLICKED.id)
+ .isEqualTo(OngoingCallLogger.OngoingCallEvents.ONGOING_CALL_CLICKED.id)
}
/** Regression test for b/212467440. */
@@ -556,8 +571,9 @@
assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
assertThat(uiEventLoggerFake.eventId(0))
- .isEqualTo(OngoingCallLogger.OngoingCallEvents.ONGOING_CALL_VISIBLE.id)
+ .isEqualTo(OngoingCallLogger.OngoingCallEvents.ONGOING_CALL_VISIBLE.id)
}
+
// Other tests for notifyChipVisibilityChanged are in [OngoingCallLogger], since
// [OngoingCallController.notifyChipVisibilityChanged] just delegates to that class.
@@ -621,8 +637,7 @@
statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = false
testScope.runCurrent()
- verify(mockSwipeStatusBarAwayGestureHandler)
- .removeOnGestureDetectedCallback(anyString())
+ verify(mockSwipeStatusBarAwayGestureHandler).removeOnGestureDetectedCallback(anyString())
}
@Test
@@ -635,8 +650,7 @@
notifCollectionListener.onEntryRemoved(ongoingCallNotifEntry, REASON_USER_STOPPED)
- verify(mockSwipeStatusBarAwayGestureHandler)
- .removeOnGestureDetectedCallback(anyString())
+ verify(mockSwipeStatusBarAwayGestureHandler).removeOnGestureDetectedCallback(anyString())
}
// TODO(b/195839150): Add test
@@ -675,5 +689,9 @@
private val hangUpIntent = mock(PendingIntent::class.java)
private val ongoingCallStyle = Notification.CallStyle.forOngoingCall(person, hangUpIntent)
-private val screeningCallStyle = Notification.CallStyle.forScreeningCall(
- person, hangUpIntent, /* answerIntent= */ mock(PendingIntent::class.java))
\ No newline at end of file
+private val screeningCallStyle =
+ Notification.CallStyle.forScreeningCall(
+ person,
+ hangUpIntent,
+ /* answerIntent= */ mock(PendingIntent::class.java),
+ )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
index 56aa7d6..73a86a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
@@ -18,6 +18,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.phone.ongoingcall.data.model.OngoingCallModel
import com.google.common.truth.Truth.assertThat
import org.junit.Test
@@ -27,12 +28,13 @@
@Test
fun hasOngoingCall_matchesSet() {
- underTest.setHasOngoingCall(true)
+ val inCallModel = OngoingCallModel.InCall(startTimeMs = 654)
+ underTest.setOngoingCallState(inCallModel)
- assertThat(underTest.hasOngoingCall.value).isTrue()
+ assertThat(underTest.ongoingCallState.value).isEqualTo(inCallModel)
- underTest.setHasOngoingCall(false)
+ underTest.setOngoingCallState(OngoingCallModel.NoCall)
- assertThat(underTest.hasOngoingCall.value).isFalse()
+ assertThat(underTest.ongoingCallState.value).isEqualTo(OngoingCallModel.NoCall)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt
index 95b132d..3de50c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt
@@ -19,6 +19,7 @@
import android.os.PersistableBundle
import android.telephony.CarrierConfigManager
import android.telephony.CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL
+import android.telephony.CarrierConfigManager.KEY_SHOW_5G_SLICE_ICON_BOOL
import android.telephony.CarrierConfigManager.KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -53,16 +54,19 @@
fun processNewConfig_updatesAllFlows() {
assertThat(underTest.shouldInflateSignalStrength.value).isFalse()
assertThat(underTest.showOperatorNameInStatusBar.value).isFalse()
+ assertThat(underTest.allowNetworkSliceIndicator.value).isTrue()
underTest.processNewCarrierConfig(
configWithOverrides(
KEY_INFLATE_SIGNAL_STRENGTH_BOOL to true,
KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL to true,
+ KEY_SHOW_5G_SLICE_ICON_BOOL to false,
)
)
assertThat(underTest.shouldInflateSignalStrength.value).isTrue()
assertThat(underTest.showOperatorNameInStatusBar.value).isTrue()
+ assertThat(underTest.allowNetworkSliceIndicator.value).isFalse()
}
@Test
@@ -79,12 +83,14 @@
configWithOverrides(
KEY_INFLATE_SIGNAL_STRENGTH_BOOL to true,
KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL to true,
+ KEY_SHOW_5G_SLICE_ICON_BOOL to true,
)
)
assertThat(underTest.isUsingDefault).isTrue()
assertThat(underTest.shouldInflateSignalStrength.value).isTrue()
assertThat(underTest.showOperatorNameInStatusBar.value).isTrue()
+ assertThat(underTest.allowNetworkSliceIndicator.value).isTrue()
// Process a new config with no keys
underTest.processNewCarrierConfig(PersistableBundle())
@@ -92,6 +98,7 @@
assertThat(underTest.isUsingDefault).isFalse()
assertThat(underTest.shouldInflateSignalStrength.value).isFalse()
assertThat(underTest.showOperatorNameInStatusBar.value).isFalse()
+ assertThat(underTest.allowNetworkSliceIndicator.value).isFalse()
}
companion object {
@@ -105,6 +112,7 @@
PersistableBundle().also {
it.putBoolean(CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL, false)
it.putBoolean(CarrierConfigManager.KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL, false)
+ it.putBoolean(CarrierConfigManager.KEY_SHOW_5G_SLICE_ICON_BOOL, true)
}
/** Override the default config with the given (key, value) pair */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
index 3695d8c..6d8bf55 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
@@ -25,6 +25,7 @@
import android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WLAN
import android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN
import android.telephony.CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL
+import android.telephony.CarrierConfigManager.KEY_SHOW_5G_SLICE_ICON_BOOL
import android.telephony.NetworkRegistrationInfo
import android.telephony.NetworkRegistrationInfo.DOMAIN_PS
import android.telephony.NetworkRegistrationInfo.REGISTRATION_STATE_DENIED
@@ -1044,6 +1045,24 @@
}
@Test
+ fun allowNetworkSliceIndicator_exposesCarrierConfigValue() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.allowNetworkSliceIndicator)
+
+ systemUiCarrierConfig.processNewCarrierConfig(
+ configWithOverride(KEY_SHOW_5G_SLICE_ICON_BOOL, true)
+ )
+
+ assertThat(latest).isTrue()
+
+ systemUiCarrierConfig.processNewCarrierConfig(
+ configWithOverride(KEY_SHOW_5G_SLICE_ICON_BOOL, false)
+ )
+
+ assertThat(latest).isFalse()
+ }
+
+ @Test
fun isAllowedDuringAirplaneMode_alwaysFalse() =
testScope.runTest {
val latest by collectLastValue(underTest.isAllowedDuringAirplaneMode)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
index dfe8023..1488418 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
@@ -194,6 +194,50 @@
}
@Test
+ fun networkSlice_configOn_hasPrioritizedCaps_showsSlice() =
+ testScope.runTest {
+ connectionRepository.allowNetworkSliceIndicator.value = true
+ val latest by collectLastValue(underTest.showSliceAttribution)
+
+ connectionRepository.hasPrioritizedNetworkCapabilities.value = true
+
+ assertThat(latest).isTrue()
+ }
+
+ @Test
+ fun networkSlice_configOn_noPrioritizedCaps_noSlice() =
+ testScope.runTest {
+ connectionRepository.allowNetworkSliceIndicator.value = true
+ val latest by collectLastValue(underTest.showSliceAttribution)
+
+ connectionRepository.hasPrioritizedNetworkCapabilities.value = false
+
+ assertThat(latest).isFalse()
+ }
+
+ @Test
+ fun networkSlice_configOff_hasPrioritizedCaps_noSlice() =
+ testScope.runTest {
+ connectionRepository.allowNetworkSliceIndicator.value = false
+ val latest by collectLastValue(underTest.showSliceAttribution)
+
+ connectionRepository.hasPrioritizedNetworkCapabilities.value = true
+
+ assertThat(latest).isFalse()
+ }
+
+ @Test
+ fun networkSlice_configOff_noPrioritizedCaps_noSlice() =
+ testScope.runTest {
+ connectionRepository.allowNetworkSliceIndicator.value = false
+ val latest by collectLastValue(underTest.showSliceAttribution)
+
+ connectionRepository.hasPrioritizedNetworkCapabilities.value = false
+
+ assertThat(latest).isFalse()
+ }
+
+ @Test
fun iconGroup_three_g() =
testScope.runTest {
connectionRepository.resolvedNetworkType.value =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
index d24d87c6..890a2e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
@@ -34,6 +34,7 @@
import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN
import android.telephony.satellite.SatelliteManager.SatelliteException
import android.telephony.satellite.SatelliteModemStateCallback
+import android.telephony.satellite.SatelliteProvisionStateCallback
import android.telephony.satellite.SatelliteSupportedStateCallback
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -326,6 +327,98 @@
}
@Test
+ fun satelliteProvisioned_notSupported_defaultFalse() =
+ testScope.runTest {
+ // GIVEN satellite is not supported
+ setUpRepo(
+ uptime = MIN_UPTIME,
+ satMan = satelliteManager,
+ satelliteSupported = false,
+ )
+
+ assertThat(underTest.isSatelliteProvisioned.value).isFalse()
+ }
+
+ @Test
+ fun satelliteProvisioned_supported_defaultFalse() =
+ testScope.runTest {
+ // GIVEN satellite is supported
+ setUpRepo(
+ uptime = MIN_UPTIME,
+ satMan = satelliteManager,
+ satelliteSupported = true,
+ )
+
+ // THEN default provisioned state is false
+ assertThat(underTest.isSatelliteProvisioned.value).isFalse()
+ }
+
+ @Test
+ fun satelliteProvisioned_supported_tracksCallback() =
+ testScope.runTest {
+ // GIVEN satellite is not supported
+ setUpRepo(
+ uptime = MIN_UPTIME,
+ satMan = satelliteManager,
+ satelliteSupported = true,
+ )
+
+ val provisioned by collectLastValue(underTest.isSatelliteProvisioned)
+ runCurrent()
+
+ val callback =
+ withArgCaptor<SatelliteProvisionStateCallback> {
+ verify(satelliteManager).registerForProvisionStateChanged(any(), capture())
+ }
+
+ // WHEN provisioning state changes
+ callback.onSatelliteProvisionStateChanged(true)
+
+ // THEN the value is reflected in the repo
+ assertThat(provisioned).isTrue()
+ }
+
+ @Test
+ fun satelliteProvisioned_supported_tracksCallback_reRegistersOnCrash() =
+ testScope.runTest {
+ // GIVEN satellite is supported
+ setUpRepo(
+ uptime = MIN_UPTIME,
+ satMan = satelliteManager,
+ satelliteSupported = true,
+ )
+
+ val provisioned by collectLastValue(underTest.isSatelliteProvisioned)
+
+ runCurrent()
+
+ val callback =
+ withArgCaptor<SatelliteProvisionStateCallback> {
+ verify(satelliteManager).registerForProvisionStateChanged(any(), capture())
+ }
+ val telephonyCallback =
+ MobileTelephonyHelpers.getTelephonyCallbackForType<
+ TelephonyCallback.RadioPowerStateListener
+ >(
+ telephonyManager
+ )
+
+ // GIVEN satellite is currently provisioned
+ callback.onSatelliteProvisionStateChanged(true)
+
+ assertThat(provisioned).isTrue()
+
+ // WHEN a crash event happens (detected by radio state change)
+ telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_ON)
+ runCurrent()
+ telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_OFF)
+ runCurrent()
+
+ // THEN listeners are re-registered
+ verify(satelliteManager, times(2)).registerForProvisionStateChanged(any(), any())
+ }
+
+ @Test
fun satelliteNotSupported_listenersAreNotRegistered() =
testScope.runTest {
// GIVEN satellite is not supported
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/FakeDeviceBasedSatelliteRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/FakeDeviceBasedSatelliteRepository.kt
index 5fa2d33..55460bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/FakeDeviceBasedSatelliteRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/FakeDeviceBasedSatelliteRepository.kt
@@ -21,6 +21,8 @@
import kotlinx.coroutines.flow.MutableStateFlow
class FakeDeviceBasedSatelliteRepository() : DeviceBasedSatelliteRepository {
+ override val isSatelliteProvisioned = MutableStateFlow(true)
+
override val connectionState = MutableStateFlow(Off)
override val signalStrength = MutableStateFlow(0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
index d303976..2e5ebb3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
@@ -31,8 +31,6 @@
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
-import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository
-import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
@@ -55,9 +53,6 @@
)
private val repo = FakeDeviceBasedSatelliteRepository()
- private val deviceProvisionedRepository = FakeDeviceProvisioningRepository()
- private val deviceProvisioningInteractor =
- DeviceProvisioningInteractor(deviceProvisionedRepository)
private val connectivityRepository = FakeConnectivityRepository()
private val wifiRepository = FakeWifiRepository()
private val wifiInteractor =
@@ -69,7 +64,6 @@
DeviceBasedSatelliteInteractor(
repo,
iconsInteractor,
- deviceProvisioningInteractor,
wifiInteractor,
testScope.backgroundScope,
FakeLogBuffer.Factory.create(),
@@ -113,7 +107,6 @@
DeviceBasedSatelliteInteractor(
repo,
iconsInteractor,
- deviceProvisioningInteractor,
wifiInteractor,
testScope.backgroundScope,
FakeLogBuffer.Factory.create(),
@@ -162,7 +155,6 @@
DeviceBasedSatelliteInteractor(
repo,
iconsInteractor,
- deviceProvisioningInteractor,
wifiInteractor,
testScope.backgroundScope,
FakeLogBuffer.Factory.create(),
@@ -219,7 +211,6 @@
DeviceBasedSatelliteInteractor(
repo,
iconsInteractor,
- deviceProvisioningInteractor,
wifiInteractor,
testScope.backgroundScope,
FakeLogBuffer.Factory.create(),
@@ -538,7 +529,6 @@
DeviceBasedSatelliteInteractor(
repo,
iconsInteractor,
- deviceProvisioningInteractor,
wifiInteractor,
testScope.backgroundScope,
FakeLogBuffer.Factory.create(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
index 43b9568..c39e301 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
@@ -32,8 +32,6 @@
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
-import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository
-import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
@@ -55,9 +53,6 @@
private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
- private val deviceProvisionedRepository = FakeDeviceProvisioningRepository()
- private val deviceProvisioningInteractor =
- DeviceProvisioningInteractor(deviceProvisionedRepository)
private val connectivityRepository = FakeConnectivityRepository()
private val wifiRepository = FakeWifiRepository()
private val wifiInteractor =
@@ -72,7 +67,6 @@
DeviceBasedSatelliteInteractor(
repo,
mobileIconsInteractor,
- deviceProvisioningInteractor,
wifiInteractor,
testScope.backgroundScope,
FakeLogBuffer.Factory.create(),
@@ -252,14 +246,14 @@
// GIVEN apm is disabled
airplaneModeRepository.setIsAirplaneMode(false)
- // GIVEN device is not provisioned
- deviceProvisionedRepository.setDeviceProvisioned(false)
+ // GIVEN satellite is not provisioned
+ repo.isSatelliteProvisioned.value = false
// THEN icon is null because the device is not provisioned
assertThat(latest).isNull()
- // GIVEN device becomes provisioned
- deviceProvisionedRepository.setDeviceProvisioned(true)
+ // GIVEN satellite becomes provisioned
+ repo.isSatelliteProvisioned.value = true
// Wait for delay to be completed
advanceTimeBy(10.seconds)
@@ -285,8 +279,8 @@
// GIVEN apm is disabled
airplaneModeRepository.setIsAirplaneMode(false)
- // GIVEN device is provisioned
- deviceProvisionedRepository.setDeviceProvisioned(true)
+ // GIVEN satellite is provisioned
+ repo.isSatelliteProvisioned.value = true
// GIVEN wifi network is active
wifiRepository.setWifiNetwork(WifiNetworkModel.Active(networkId = 0, level = 1))
@@ -474,14 +468,14 @@
// GIVEN apm is disabled
airplaneModeRepository.setIsAirplaneMode(false)
- // GIVEN device is not provisioned
- deviceProvisionedRepository.setDeviceProvisioned(false)
+ // GIVEN satellite is not provisioned
+ repo.isSatelliteProvisioned.value = false
// THEN carrier text is null because the device is not provisioned
assertThat(latest).isNull()
- // GIVEN device becomes provisioned
- deviceProvisionedRepository.setDeviceProvisioned(true)
+ // GIVEN satellite becomes provisioned
+ repo.isSatelliteProvisioned.value = true
// Wait for delay to be completed
advanceTimeBy(10.seconds)
@@ -508,8 +502,8 @@
// GIVEN apm is disabled
airplaneModeRepository.setIsAirplaneMode(false)
- // GIVEN device is provisioned
- deviceProvisionedRepository.setDeviceProvisioned(true)
+ // GIVEN satellite is provisioned
+ repo.isSatelliteProvisioned.value = true
// GIVEN wifi network is active
wifiRepository.setWifiNetwork(WifiNetworkModel.Active(networkId = 0, level = 1))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
index c9fe449..b8299e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
@@ -29,6 +29,7 @@
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.assertLogsWtf
@@ -37,8 +38,10 @@
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
import com.android.systemui.screenrecord.data.repository.screenRecordRepository
import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
-import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsMediaProjectionChip
+import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE
+import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsScreenRecordChip
+import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsShareToAppChip
import com.android.systemui.statusbar.chips.ui.viewmodel.ongoingActivityChipsViewModel
import com.android.systemui.statusbar.data.model.StatusBarMode
import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository.Companion.DISPLAY_ID
@@ -55,12 +58,16 @@
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
+import org.junit.Before
import org.junit.Test
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
class CollapsedStatusBarViewModelImplTest : SysuiTestCase() {
- private val kosmos = Kosmos().apply { testDispatcher = UnconfinedTestDispatcher() }
+ private val kosmos = Kosmos().also {
+ it.testCase = this
+ it.testDispatcher = UnconfinedTestDispatcher()
+ }
private val testScope = kosmos.testScope
@@ -77,6 +84,11 @@
kosmos.applicationCoroutineScope,
)
+ @Before
+ fun setUp() {
+ setUpPackageManagerForMediaProjection(kosmos)
+ }
+
@Test
fun isTransitioningFromLockscreenToOccluded_started_isTrue() =
testScope.runTest {
@@ -405,9 +417,9 @@
assertThat(latest).isEqualTo(OngoingActivityChipModel.Hidden)
kosmos.fakeMediaProjectionRepository.mediaProjectionState.value =
- MediaProjectionState.EntireScreen
+ MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
- assertIsMediaProjectionChip(latest)
+ assertIsShareToAppChip(latest)
}
private fun activeNotificationsStore(notifications: List<ActiveNotificationModel>) =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index dbdbe65..fac6a4c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -87,6 +87,7 @@
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.volume.domain.interactor.VolumePanelNavigationInteractor;
+import com.android.systemui.volume.panel.shared.flag.VolumePanelFlag;
import com.android.systemui.volume.ui.binder.VolumeDialogMenuIconBinder;
import com.android.systemui.volume.ui.navigation.VolumeNavigator;
@@ -119,7 +120,6 @@
View mDrawerNormal;
ViewGroup mDialogRowsView;
CaptionsToggleImageButton mODICaptionsIcon;
-
private TestableLooper mTestableLooper;
private ConfigurationController mConfigurationController;
private int mOriginalOrientation;
@@ -151,6 +151,8 @@
private VolumeNavigator mVolumeNavigator;
@Mock
private VolumeDialogMenuIconBinder mVolumeDialogMenuIconBinder;
+ @Mock
+ private VolumePanelFlag mVolumePanelFlag;
private final CsdWarningDialog.Factory mCsdWarningDialogFactory =
new CsdWarningDialog.Factory() {
@@ -211,6 +213,7 @@
mCsdWarningDialogFactory,
mPostureController,
mTestableLooper.getLooper(),
+ mVolumePanelFlag,
mDumpManager,
mLazySecureSettings,
mVibratorHelper,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt
index 5a092f3..976a19c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt
@@ -16,16 +16,19 @@
package com.android.systemui.animation
+import android.content.applicationContext
import com.android.systemui.jank.interactionJankMonitor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.kosmos.testCase
+import com.android.systemui.util.mockito.mock
val Kosmos.dialogTransitionAnimator by Fixture {
fakeDialogTransitionAnimator(
// The main thread is checked in a bunch of places inside the different transitions
// animators, so we have to pass the real main executor here.
- mainExecutor = testCase.context.mainExecutor,
+ mainExecutor = applicationContext.mainExecutor,
interactionJankMonitor = interactionJankMonitor,
)
}
+
+val Kosmos.mockDialogTransitionAnimator by Fixture { mock<DialogTransitionAnimator>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalSceneRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalSceneRepository.kt
index a7bf87d..d280be2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalSceneRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalSceneRepository.kt
@@ -6,6 +6,7 @@
import com.android.systemui.communal.shared.model.CommunalScenes
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
@@ -13,20 +14,25 @@
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
/** Fake implementation of [CommunalSceneRepository]. */
@OptIn(ExperimentalCoroutinesApi::class)
class FakeCommunalSceneRepository(
- applicationScope: CoroutineScope,
+ private val applicationScope: CoroutineScope,
override val currentScene: MutableStateFlow<SceneKey> =
MutableStateFlow(CommunalScenes.Default),
) : CommunalSceneRepository {
- override fun changeScene(toScene: SceneKey, transitionKey: TransitionKey?) =
- snapToScene(toScene)
- override fun snapToScene(toScene: SceneKey) {
- this.currentScene.value = toScene
- this._transitionState.value = flowOf(ObservableTransitionState.Idle(toScene))
+ override fun changeScene(toScene: SceneKey, transitionKey: TransitionKey?) =
+ snapToScene(toScene, 0)
+
+ override fun snapToScene(toScene: SceneKey, delayMillis: Long) {
+ applicationScope.launch {
+ delay(delayMillis)
+ currentScene.value = toScene
+ _transitionState.value = flowOf(ObservableTransitionState.Idle(toScene))
+ }
}
private val defaultTransitionState = ObservableTransitionState.Idle(CommunalScenes.Default)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelKosmos.kt
index e3c218d..3f38408 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelKosmos.kt
@@ -17,6 +17,7 @@
package com.android.systemui.communal.ui.viewmodel
import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.communal.util.communalColors
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.ui.viewmodel.dreamingToGlanceableHubTransitionViewModel
@@ -37,6 +38,7 @@
dreamToGlanceableHubTransitionViewModel = dreamingToGlanceableHubTransitionViewModel,
glanceableHubToDreamTransitionViewModel = glanceableHubToDreamingTransitionViewModel,
communalInteractor = communalInteractor,
+ communalSceneInteractor = communalSceneInteractor,
keyguardTransitionInteractor = keyguardTransitionInteractor,
communalColors = communalColors,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/qs/QSLongPressEffectKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/qs/QSLongPressEffectKosmos.kt
index 24603ef..eff99e04 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/qs/QSLongPressEffectKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/qs/QSLongPressEffectKosmos.kt
@@ -17,8 +17,8 @@
package com.android.systemui.haptics.qs
import com.android.systemui.haptics.vibratorHelper
-import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.policy.keyguardStateController
val Kosmos.qsLongPressEffect by
- Kosmos.Fixture { QSLongPressEffect(vibratorHelper, keyguardInteractor) }
+ Kosmos.Fixture { QSLongPressEffect(vibratorHelper, keyguardStateController) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 22b8c8db..2d100f0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -80,7 +80,7 @@
override val isAodAvailable: StateFlow<Boolean> = _isAodAvailable
private val _isDreaming = MutableStateFlow(false)
- override val isDreaming: Flow<Boolean> = _isDreaming
+ override val isDreaming: MutableStateFlow<Boolean> = _isDreaming
private val _isDreamingWithOverlay = MutableStateFlow(false)
override val isDreamingWithOverlay: Flow<Boolean> = _isDreamingWithOverlay
@@ -204,7 +204,7 @@
_isAodAvailable.value = value
}
- fun setDreaming(isDreaming: Boolean) {
+ override fun setDreaming(isDreaming: Boolean) {
_isDreaming.value = isDreaming
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
index 162fd90..28bd439 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
@@ -16,7 +16,6 @@
package com.android.systemui.keyguard.domain.interactor
-import com.android.systemui.flags.featureFlagsClassic
import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
@@ -34,7 +33,6 @@
bgDispatcher = testDispatcher,
mainDispatcher = testDispatcher,
keyguardInteractor = keyguardInteractor,
- flags = featureFlagsClassic,
shadeRepository = shadeRepository,
powerInteractor = powerInteractor,
glanceableHubTransitions = glanceableHubTransitions,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt
index 98babff..d72b9c1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt
@@ -18,7 +18,6 @@
import com.android.keyguard.keyguardSecurityModel
import com.android.systemui.communal.domain.interactor.communalInteractor
-import com.android.systemui.flags.featureFlagsClassic
import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
@@ -37,7 +36,6 @@
mainDispatcher = testDispatcher,
keyguardInteractor = keyguardInteractor,
communalInteractor = communalInteractor,
- flags = featureFlagsClassic,
keyguardSecurityModel = keyguardSecurityModel,
selectedUserInteractor = selectedUserInteractor,
powerInteractor = powerInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
index 02842cc..b5ca964 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
@@ -24,6 +24,7 @@
import com.android.systemui.keyguard.data.repository.FakeCommandQueue
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
import com.android.systemui.scene.domain.interactor.SceneInteractor
@@ -34,6 +35,7 @@
import com.android.systemui.util.mockito.whenever
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.TestScope
/**
@@ -60,9 +62,11 @@
): WithDependencies {
// Mock these until they are replaced by kosmos
val currentKeyguardStateFlow = MutableSharedFlow<KeyguardState>()
+ val transitionStateFlow = MutableStateFlow(TransitionStep())
val keyguardTransitionInteractor =
mock<KeyguardTransitionInteractor>().also {
whenever(it.currentKeyguardState).thenReturn(currentKeyguardStateFlow)
+ whenever(it.transitionState).thenReturn(transitionStateFlow)
}
val configurationDimensionFlow = MutableSharedFlow<ConfigurationBasedDimensions>()
configurationDimensionFlow.tryEmit(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt
index 6d46694..3c62b44 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt
@@ -22,6 +22,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.notificationIconContainerAlwaysOnDisplayViewModel
import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor
import com.android.systemui.statusbar.ui.systemBarUtilsProxy
@@ -30,6 +31,7 @@
KeyguardClockViewModel(
keyguardClockInteractor = keyguardClockInteractor,
applicationScope = applicationCoroutineScope,
+ aodNotificationIconViewModel = notificationIconContainerAlwaysOnDisplayViewModel,
notifsKeyguardInteractor = notificationsKeyguardInteractor,
shadeInteractor = shadeInteractor,
systemBarUtils = systemBarUtilsProxy,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index 6d2d04a..67f8443 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -29,6 +29,7 @@
import com.android.systemui.common.ui.domain.interactor.configurationInteractor
import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.communal.ui.viewmodel.communalTransitionViewModel
import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
@@ -62,6 +63,8 @@
import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
import com.android.systemui.statusbar.phone.scrimController
import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.fakeWifiRepository
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.wifiInteractor
import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvisioningRepository
import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
import com.android.systemui.util.time.systemClock
@@ -111,6 +114,7 @@
val deviceEntryUdfpsInteractor by lazy { kosmos.deviceEntryUdfpsInteractor }
val deviceUnlockedInteractor by lazy { kosmos.deviceUnlockedInteractor }
val communalInteractor by lazy { kosmos.communalInteractor }
+ val communalSceneInteractor by lazy { kosmos.communalSceneInteractor }
val sceneContainerPlugin by lazy { kosmos.sceneContainerPlugin }
val deviceProvisioningInteractor by lazy { kosmos.deviceProvisioningInteractor }
val fakeDeviceProvisioningRepository by lazy { kosmos.fakeDeviceProvisioningRepository }
@@ -130,6 +134,8 @@
val shadeController by lazy { kosmos.shadeController }
val shadeRepository by lazy { kosmos.shadeRepository }
val shadeInteractor by lazy { kosmos.shadeInteractor }
+ val wifiInteractor by lazy { kosmos.wifiInteractor }
+ val fakeWifiRepository by lazy { kosmos.fakeWifiRepository }
val ongoingActivityChipsViewModel by lazy { kosmos.ongoingActivityChipsViewModel }
val scrimController by lazy { kosmos.scrimController }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractorKosmos.kt
index e5e2aff..ca1b3f5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractorKosmos.kt
@@ -18,7 +18,6 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
-import com.android.systemui.media.controls.data.repository.mediaDataRepository
import com.android.systemui.media.controls.data.repository.mediaFilterRepository
import com.android.systemui.media.controls.domain.pipeline.mediaDataCombineLatest
import com.android.systemui.media.controls.domain.pipeline.mediaDataFilter
@@ -33,7 +32,6 @@
Kosmos.Fixture {
MediaCarouselInteractor(
applicationScope = applicationCoroutineScope,
- mediaDataRepository = mediaDataRepository,
mediaDataProcessor = mediaDataProcessor,
mediaTimeoutListener = mediaTimeoutListener,
mediaResumeListener = mediaResumeListener,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt
index c4365c9..d631f92 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt
@@ -25,4 +25,10 @@
override val mediaProjectionState: MutableStateFlow<MediaProjectionState> =
MutableStateFlow(MediaProjectionState.NotProjecting)
+
+ var stopProjectingInvoked = false
+
+ override suspend fun stopProjecting() {
+ stopProjectingInvoked = true
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneContainerStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneContainerStartableKosmos.kt
index d82286f..cf18c0e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneContainerStartableKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneContainerStartableKosmos.kt
@@ -26,6 +26,7 @@
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.windowManagerLockscreenVisibilityInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.testScope
@@ -67,5 +68,6 @@
uiEventLogger = uiEventLogger,
sceneBackInteractor = sceneBackInteractor,
shadeSessionStorage = shadeSessionStorage,
+ windowMgrLockscreenVisInteractor = windowManagerLockscreenVisibilityInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt
index d1fbb5e..066736c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt
@@ -16,16 +16,12 @@
package com.android.systemui.scene.domain.interactor
-import com.android.compose.animation.scene.SceneKey
-import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.scene.data.repository.sceneContainerRepository
-import com.android.systemui.scene.domain.resolver.HomeSceneFamilyResolver
-import com.android.systemui.scene.domain.resolver.SceneResolver
+import com.android.systemui.scene.domain.resolver.sceneFamilyResolvers
import com.android.systemui.scene.shared.logger.sceneLogger
-import com.android.systemui.scene.shared.model.SceneFamilies
val Kosmos.sceneInteractor by
Kosmos.Fixture {
@@ -37,14 +33,3 @@
deviceUnlockedInteractor = deviceUnlockedInteractor,
)
}
-
-val Kosmos.sceneFamilyResolvers: Map<SceneKey, SceneResolver>
- get() = mapOf(SceneFamilies.Home to homeSceneFamilyResolver)
-
-val Kosmos.homeSceneFamilyResolver by
- Kosmos.Fixture {
- HomeSceneFamilyResolver(
- applicationScope = applicationCoroutineScope,
- deviceEntryInteractor = deviceEntryInteractor,
- )
- }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt
new file mode 100644
index 0000000..6be1939
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.scene.domain.resolver
+
+import com.android.compose.animation.scene.SceneKey
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.scene.shared.model.SceneFamilies
+import com.android.systemui.shade.domain.interactor.shadeInteractor
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+val Kosmos.sceneFamilyResolvers: Map<SceneKey, SceneResolver>
+ get() =
+ mapOf(
+ SceneFamilies.Home to homeSceneFamilyResolver,
+ SceneFamilies.NotifShade to notifShadeSceneFamilyResolver,
+ SceneFamilies.QuickSettings to quickSettingsSceneFamilyResolver,
+ )
+
+val Kosmos.homeSceneFamilyResolver by
+ Kosmos.Fixture {
+ HomeSceneFamilyResolver(
+ applicationScope = applicationCoroutineScope,
+ deviceEntryInteractor = deviceEntryInteractor,
+ )
+ }
+
+val Kosmos.notifShadeSceneFamilyResolver by
+ Kosmos.Fixture {
+ NotifShadeSceneFamilyResolver(
+ applicationScope = applicationCoroutineScope,
+ shadeInteractor = shadeInteractor,
+ )
+ }
+
+val Kosmos.quickSettingsSceneFamilyResolver by
+ Kosmos.Fixture {
+ QuickSettingsSceneFamilyResolver(
+ applicationScope = applicationCoroutineScope,
+ shadeInteractor = shadeInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/screenrecord/data/repository/FakeScreenRecordRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/screenrecord/data/repository/FakeScreenRecordRepository.kt
index fb0e368..30b4763 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/screenrecord/data/repository/FakeScreenRecordRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/screenrecord/data/repository/FakeScreenRecordRepository.kt
@@ -22,4 +22,10 @@
class FakeScreenRecordRepository : ScreenRecordRepository {
override val screenRecordState: MutableStateFlow<ScreenRecordModel> =
MutableStateFlow(ScreenRecordModel.DoingNothing)
+
+ var stopRecordingInvoked = false
+
+ override suspend fun stopRecording() {
+ stopRecordingInvoked = true
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt
index cc836ac..0bc4d54 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt
@@ -23,7 +23,6 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
-import com.android.systemui.log.LogBuffer
import com.android.systemui.plugins.statusbar.statusBarStateController
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
@@ -50,7 +49,6 @@
shadeInteractor = shadeInteractor,
sceneInteractor = sceneInteractor,
notificationStackScrollLayout = mock<NotificationStackScrollLayout>(),
- touchLog = mock<LogBuffer>(),
vibratorHelper = mock<VibratorHelper>(),
commandQueue = mock<CommandQueue>(),
statusBarKeyguardViewManager = mock<StatusBarKeyguardViewManager>(),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt
index 0a3a2ee..bcea983 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt
@@ -25,7 +25,6 @@
val Kosmos.shadeLockscreenInteractor by
Kosmos.Fixture {
ShadeLockscreenInteractorImpl(
- applicationScope = applicationCoroutineScope,
backgroundScope = applicationCoroutineScope,
shadeInteractor = shadeInteractorImpl,
sceneInteractor = sceneInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt
index 8d653f7..0e21698 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt
@@ -21,6 +21,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.plugins.activityStarter
+import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.shade.domain.interactor.privacyChipInteractor
import com.android.systemui.shade.domain.interactor.shadeHeaderClockInteractor
import com.android.systemui.shade.domain.interactor.shadeInteractor
@@ -33,6 +34,7 @@
applicationScope = applicationCoroutineScope,
context = applicationContext,
activityStarter = activityStarter,
+ sceneInteractor = sceneInteractor,
shadeInteractor = shadeInteractor,
mobileIconsInteractor = mobileIconsInteractor,
mobileIconsViewModel = mobileIconsViewModel,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorKosmos.kt
new file mode 100644
index 0000000..9d22811
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorKosmos.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips.mediaprojection.domain.interactor
+
+import android.content.packageManager
+import com.android.systemui.animation.mockDialogTransitionAnimator
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
+import com.android.systemui.statusbar.chips.mediaprojection.ui.view.endMediaProjectionDialogHelper
+import com.android.systemui.util.time.fakeSystemClock
+
+val Kosmos.mediaProjectionChipInteractor: MediaProjectionChipInteractor by
+ Kosmos.Fixture {
+ MediaProjectionChipInteractor(
+ scope = applicationCoroutineScope,
+ mediaProjectionRepository = fakeMediaProjectionRepository,
+ packageManager = packageManager,
+ systemClock = fakeSystemClock,
+ endMediaProjectionDialogHelper = endMediaProjectionDialogHelper,
+ dialogTransitionAnimator = mockDialogTransitionAnimator,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperKosmos.kt
new file mode 100644
index 0000000..4f82662
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperKosmos.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.mediaprojection.ui.view
+
+import android.content.applicationContext
+import android.content.packageManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
+
+val Kosmos.endMediaProjectionDialogHelper: EndMediaProjectionDialogHelper by
+ Kosmos.Fixture {
+ EndMediaProjectionDialogHelper(
+ dialogFactory = mockSystemUIDialogFactory,
+ packageManager = packageManager,
+ context = applicationContext,
+ )
+ }
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 88bde2e..51ec540 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt
@@ -16,13 +16,14 @@
package com.android.systemui.statusbar.chips.ui.viewmodel
+import com.android.systemui.animation.mockDialogTransitionAnimator
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testScope
-import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
import com.android.systemui.screenrecord.data.repository.screenRecordRepository
-import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractor
+import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.mediaProjectionChipInteractor
import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.ScreenRecordChipInteractor
+import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
import com.android.systemui.util.time.fakeSystemClock
val Kosmos.screenRecordChipInteractor: ScreenRecordChipInteractor by
@@ -30,15 +31,8 @@
ScreenRecordChipInteractor(
scope = applicationCoroutineScope,
screenRecordRepository = screenRecordRepository,
- systemClock = fakeSystemClock,
- )
- }
-
-val Kosmos.mediaProjectionChipInteractor: MediaProjectionChipInteractor by
- Kosmos.Fixture {
- MediaProjectionChipInteractor(
- scope = applicationCoroutineScope,
- mediaProjectionRepository = fakeMediaProjectionRepository,
+ dialogFactory = mockSystemUIDialogFactory,
+ dialogTransitionAnimator = mockDialogTransitionAnimator,
systemClock = fakeSystemClock,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
index d00eedf..299486f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
@@ -31,6 +31,7 @@
import com.android.systemui.keyguard.ui.viewmodel.goneToAodTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.goneToDozingTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.goneToDreamingTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.goneToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.lockscreenToDreamingTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.lockscreenToGlanceableHubTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.lockscreenToGoneTransitionViewModel
@@ -70,6 +71,7 @@
goneToAodTransitionViewModel = goneToAodTransitionViewModel,
goneToDozingTransitionViewModel = goneToDozingTransitionViewModel,
goneToDreamingTransitionViewModel = goneToDreamingTransitionViewModel,
+ goneToLockscreenTransitionViewModel = goneToLockscreenTransitionViewModel,
glanceableHubToLockscreenTransitionViewModel = glanceableHubToLockscreenTransitionViewModel,
lockscreenToDreamingTransitionViewModel = lockscreenToDreamingTransitionViewModel,
lockscreenToGlanceableHubTransitionViewModel = lockscreenToGlanceableHubTransitionViewModel,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryKosmos.kt
index 3bb9580..1851c89 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryKosmos.kt
@@ -21,8 +21,9 @@
import com.android.systemui.broadcast.broadcastDispatcher
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.model.sysUiState
+import com.android.systemui.util.mockito.mock
-val Kosmos.systemUIDialogFactory by
+val Kosmos.systemUIDialogFactory: SystemUIDialogFactory by
Kosmos.Fixture {
SystemUIDialogFactory(
applicationContext,
@@ -32,3 +33,6 @@
dialogTransitionAnimator,
)
}
+
+val Kosmos.mockSystemUIDialogFactory: SystemUIDialog.Factory by
+ Kosmos.Fixture { mock<SystemUIDialog.Factory>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
index eb2d6c0..c3c3cce 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
@@ -32,6 +32,7 @@
) : MobileConnectionRepository {
override val carrierId = MutableStateFlow(UNKNOWN_CARRIER_ID)
override val inflateSignalStrength: MutableStateFlow<Boolean> = MutableStateFlow(false)
+ override val allowNetworkSliceIndicator: MutableStateFlow<Boolean> = MutableStateFlow(true)
override val isEmergencyOnly = MutableStateFlow(false)
override val isRoaming = MutableStateFlow(false)
override val operatorAlphaShort: MutableStateFlow<String?> = MutableStateFlow(null)
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryKosmos.kt
similarity index 63%
copy from packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogModule.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryKosmos.kt
index 2e9169e..8e656cf 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryKosmos.kt
@@ -13,17 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.bluetooth.qsdialog
-import com.android.systemui.dagger.SysUISingleton
-import dagger.Binds
-import dagger.Module
+package com.android.systemui.statusbar.pipeline.shared.data.repository
-@Module
-interface BluetoothTileDialogModule {
- @Binds
- @SysUISingleton
- fun bindDeviceItemActionInteractor(
- impl: DeviceItemActionInteractorImpl
- ): DeviceItemActionInteractor
-}
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.fakeConnectivityRepository: FakeConnectivityRepository by
+ Kosmos.Fixture { FakeConnectivityRepository() }
+val Kosmos.connectivityRepository: ConnectivityRepository by
+ Kosmos.Fixture { fakeConnectivityRepository }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
similarity index 97%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
index 97c8d5f..709be5e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryKosmos.kt
similarity index 64%
rename from packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogModule.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryKosmos.kt
index 2e9169e..e44061a 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryKosmos.kt
@@ -13,17 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.bluetooth.qsdialog
-import com.android.systemui.dagger.SysUISingleton
-import dagger.Binds
-import dagger.Module
+package com.android.systemui.statusbar.pipeline.wifi.data.repository
-@Module
-interface BluetoothTileDialogModule {
- @Binds
- @SysUISingleton
- fun bindDeviceItemActionInteractor(
- impl: DeviceItemActionInteractorImpl
- ): DeviceItemActionInteractor
-}
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.fakeWifiRepository: FakeWifiRepository by Kosmos.Fixture { FakeWifiRepository() }
+val Kosmos.wifiRepository: WifiRepository by Kosmos.Fixture { fakeWifiRepository }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorKosmos.kt
new file mode 100644
index 0000000..7036199
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorKosmos.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.wifi.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.statusbar.pipeline.shared.data.repository.connectivityRepository
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.wifiRepository
+
+val Kosmos.wifiInteractor: WifiInteractor by
+ Kosmos.Fixture {
+ WifiInteractorImpl(
+ connectivityRepository,
+ wifiRepository,
+ applicationCoroutineScope,
+ )
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopupKosmos.kt
similarity index 61%
copy from packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogModule.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopupKosmos.kt
index 2e9169e..49170d8 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopupKosmos.kt
@@ -13,17 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.bluetooth.qsdialog
-import com.android.systemui.dagger.SysUISingleton
-import dagger.Binds
-import dagger.Module
+package com.android.systemui.volume.panel.component.popup.ui.composable
-@Module
-interface BluetoothTileDialogModule {
- @Binds
- @SysUISingleton
- fun bindDeviceItemActionInteractor(
- impl: DeviceItemActionInteractorImpl
- ): DeviceItemActionInteractor
-}
+import com.android.systemui.animation.dialogTransitionAnimator
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.phone.systemUIDialogFactory
+
+val Kosmos.volumePanelPopup: VolumePanelPopup by
+ Kosmos.Fixture { VolumePanelPopup(systemUIDialogFactory, dialogTransitionAnimator) }
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/NaturalRotation.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/NaturalRotation.kt
new file mode 100644
index 0000000..be02487
--- /dev/null
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/NaturalRotation.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.android.systemui.unfold.dagger
+
+import javax.inject.Qualifier
+
+/** Qualifier annotation for a progress provider that emits animation events only when
+ * in natural rotation */
+@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class NaturalRotation
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt
index 4f3aee9..fec6ff1 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt
@@ -27,6 +27,7 @@
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
+import java.util.concurrent.CopyOnWriteArrayList
/**
* Allows to subscribe to rotation changes. Updates are provided for the display associated to
@@ -41,7 +42,7 @@
@Assisted private val callbackHandler: Handler,
) : CallbackController<RotationChangeProvider.RotationListener> {
- private val listeners = mutableListOf<RotationListener>()
+ private val listeners = CopyOnWriteArrayList<RotationListener>()
private val displayListener = RotationDisplayListener()
private var lastRotation: Int? = null
diff --git a/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java b/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java
old mode 100755
new mode 100644
diff --git a/packages/WallpaperCropper/res/drawable-hdpi/ic_actionbar_accept.png b/packages/WallpaperCropper/res/drawable-hdpi/ic_actionbar_accept.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/packages/WallpaperCropper/res/drawable-mdpi/ic_actionbar_accept.png b/packages/WallpaperCropper/res/drawable-mdpi/ic_actionbar_accept.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/packages/WallpaperCropper/res/drawable-xhdpi/ic_actionbar_accept.png b/packages/WallpaperCropper/res/drawable-xhdpi/ic_actionbar_accept.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/proto/src/ondeviceintelligence/OWNERS b/proto/src/ondeviceintelligence/OWNERS
new file mode 100644
index 0000000..09774f7
--- /dev/null
+++ b/proto/src/ondeviceintelligence/OWNERS
@@ -0,0 +1 @@
+file:/core/java/android/app/ondeviceintelligence/OWNERS
diff --git a/proto/src/ondeviceintelligence/inference_info.proto b/proto/src/ondeviceintelligence/inference_info.proto
new file mode 100644
index 0000000..a6f4f4f
--- /dev/null
+++ b/proto/src/ondeviceintelligence/inference_info.proto
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+
+package android.ondeviceintelligence;
+
+option java_package = "com.android.server.ondeviceintelligence";
+option java_multiple_files = true;
+
+
+message InferenceInfo {
+ // Uid for the caller app.
+ optional int32 uid = 1;
+ // Inference start time(milliseconds from the epoch time).
+ optional int64 start_time_ms = 2;
+ // Inference end time(milliseconds from the epoch time).
+ optional int64 end_time_ms = 3;
+ // Suspended time in milliseconds.
+ optional int64 suspended_time_ms = 4;
+}
\ No newline at end of file
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index 9593006..f85d786 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -101,8 +101,10 @@
SystemActionPerformer systemActionPerformer,
AccessibilityWindowManager awm, int flags) {
accessibilityServiceInfo.setComponentName(COMPONENT_NAME);
- Slogf.i(LOG_TAG, "Registering UiTestAutomationService (id=%s) when called by user %d",
- accessibilityServiceInfo.getId(), Binder.getCallingUserHandle().getIdentifier());
+ Slogf.i(LOG_TAG, "Registering UiTestAutomationService (id=%s, flags=0x%x) when"
+ + " called by user %d",
+ accessibilityServiceInfo.getId(), flags,
+ Binder.getCallingUserHandle().getIdentifier());
if (mUiAutomationService != null) {
throw new IllegalStateException(
"UiAutomationService " + mUiAutomationService.mServiceInterface
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 6fc05b7..eae516e 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -2003,7 +2003,7 @@
final AutofillManagerServiceImpl service =
peekServiceForUserWithLocalBinderIdentityLocked(userId);
if (service != null) {
- service.setViewAutofilled(sessionId, getCallingUid(), id);
+ service.setViewAutofilledLocked(sessionId, getCallingUid(), id);
} else if (sVerbose) {
Slog.v(TAG, "setAutofillFailure(): no service for " + userId);
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 588266f..2bf319e 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -478,7 +478,7 @@
}
@GuardedBy("mLock")
- void setViewAutofilled(int sessionId, int uid, @NonNull AutofillId id) {
+ void setViewAutofilledLocked(int sessionId, int uid, @NonNull AutofillId id) {
if (!isEnabledLocked()) {
Slog.wtf(TAG, "Service not enabled");
return;
@@ -488,7 +488,7 @@
Slog.v(TAG, "setViewAutofilled(): no session for " + sessionId + "(" + uid + ")");
return;
}
- session.setViewAutofilled(id);
+ session.setViewAutofilledLocked(id);
}
@GuardedBy("mLock")
@@ -515,7 +515,10 @@
}
final boolean finished = saveResult.isRemoveSession();
- if (sVerbose) Slog.v(TAG, "finishSessionLocked(): session finished on save? " + finished);
+ if (sVerbose) {
+ Slog.v(TAG, "finishSessionLocked(): session finished? " + finished
+ + ", showing save UI? " + saveResult.isLogSaveShown());
+ }
if (finished) {
session.removeFromServiceLocked();
@@ -792,10 +795,9 @@
* Initializes the last fill selection after an autofill service returned a new
* {@link FillResponse}.
*/
- void setLastResponse(int sessionId, @NonNull FillResponse response) {
- synchronized (mLock) {
+ @GuardedBy("mLock")
+ void setLastResponseLocked(int sessionId, @NonNull FillResponse response) {
mEventHistory = new FillEventHistory(sessionId, response.getClientState());
- }
}
void setLastAugmentedAutofillResponse(int sessionId) {
diff --git a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
index aa76200..676abd1 100644
--- a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
@@ -641,6 +641,15 @@
}
/**
+ * Set how many views are filtered from fill because they are not in current session
+ */
+ public void maybeSetFilteredFillableViewsCount(int filteredViewsCount) {
+ mEventInternal.ifPresent(event -> {
+ event.mFilteredFillabaleViewCount = filteredViewsCount;
+ });
+ }
+
+ /**
* Set views_filled_failure_count using failure count as long as mEventInternal
* presents.
*/
@@ -675,7 +684,14 @@
} else if (autofillIds.contains(autofillId)) {
if (sVerbose) {
Slog.v(TAG, "Logging autofill for id:" + autofillId);
- event.mViewFillSuccessCount++;
+ }
+ event.mViewFillSuccessCount++;
+ autofillIds.remove(autofillId);
+ event.mAlreadyFilledAutofillIds.add(autofillId);
+ } else if (event.mAlreadyFilledAutofillIds.contains(autofillId)) {
+ if (sVerbose) {
+ Slog.v(TAG, "Successfully filled autofillId:" + autofillId
+ + " already processed ");
}
} else {
Slog.w(TAG, "Successfully filled autofillId:" + autofillId
@@ -728,6 +744,7 @@
+ " mAppPackageUid=" + mCallingAppUid
+ " mIsCredentialRequest=" + event.mIsCredentialRequest
+ " mWebviewRequestedCredential=" + event.mWebviewRequestedCredential
+ + " mFilteredFillabaleViewCount=" + event.mFilteredFillabaleViewCount
+ " mViewFillableTotalCount=" + event.mViewFillableTotalCount
+ " mViewFillFailureCount=" + event.mViewFillFailureCount
+ " mFocusedId=" + event.mFocusedId
@@ -784,6 +801,7 @@
mCallingAppUid,
event.mIsCredentialRequest,
event.mWebviewRequestedCredential,
+ event.mFilteredFillabaleViewCount,
event.mViewFillableTotalCount,
event.mViewFillFailureCount,
event.mFocusedId,
@@ -832,6 +850,7 @@
int mFieldClassificationRequestId = DEFAULT_VALUE_INT;
boolean mIsCredentialRequest = false;
boolean mWebviewRequestedCredential = false;
+ int mFilteredFillabaleViewCount = DEFAULT_VALUE_INT;
int mViewFillableTotalCount = DEFAULT_VALUE_INT;
int mViewFillFailureCount = DEFAULT_VALUE_INT;
int mFocusedId = DEFAULT_VALUE_INT;
@@ -850,6 +869,7 @@
int mViewFilledButUnexpectedCount = 0;
ArraySet<AutofillId> mAutofillIdsAttemptedAutofill;
+ ArraySet<AutofillId> mAlreadyFilledAutofillIds = new ArraySet<>();
PresentationStatsEventInternal() {}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index aa67ffe..b22f999 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1677,22 +1677,23 @@
final LogMaker requestLog;
- // Start a new FillResponse logger for the success case.
- mFillResponseEventLogger.startLogForNewResponse();
- mFillResponseEventLogger.maybeSetRequestId(requestId);
- mFillResponseEventLogger.maybeSetAppPackageUid(uid);
- mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_SUCCESS);
- mFillResponseEventLogger.startResponseProcessingTime();
- // Time passed since session was created
- final long fillRequestReceivedRelativeTimestamp =
- SystemClock.elapsedRealtime() - mLatencyBaseTime;
- mPresentationStatsEventLogger.maybeSetFillResponseReceivedTimestampMs(
- (int) (fillRequestReceivedRelativeTimestamp));
- mFillResponseEventLogger.maybeSetLatencyFillResponseReceivedMillis(
- (int) (fillRequestReceivedRelativeTimestamp));
- mFillResponseEventLogger.maybeSetDetectionPreference(getDetectionPreferenceForLogging());
-
synchronized (mLock) {
+ // Start a new FillResponse logger for the success case.
+ mFillResponseEventLogger.startLogForNewResponse();
+ mFillResponseEventLogger.maybeSetRequestId(requestId);
+ mFillResponseEventLogger.maybeSetAppPackageUid(uid);
+ mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_SUCCESS);
+ mFillResponseEventLogger.startResponseProcessingTime();
+ // Time passed since session was created
+ final long fillRequestReceivedRelativeTimestamp =
+ SystemClock.elapsedRealtime() - mLatencyBaseTime;
+ mPresentationStatsEventLogger.maybeSetFillResponseReceivedTimestampMs(
+ (int) (fillRequestReceivedRelativeTimestamp));
+ mFillResponseEventLogger.maybeSetLatencyFillResponseReceivedMillis(
+ (int) (fillRequestReceivedRelativeTimestamp));
+ mFillResponseEventLogger.maybeSetDetectionPreference(
+ getDetectionPreferenceForLogging());
+
if (mDestroyed) {
Slog.w(TAG, "Call to Session#onFillRequestSuccess() rejected - session: "
+ id + " destroyed");
@@ -1744,11 +1745,9 @@
Slog.v(TAG, "Service requested to wait for delayed fill response.");
registerDelayedFillBroadcastLocked();
}
- }
- mService.setLastResponse(id, response);
+ mService.setLastResponseLocked(id, response);
- synchronized (mLock) {
if (mLogViewEntered) {
mLogViewEntered = false;
mService.logViewEntered(id, null);
@@ -1821,16 +1820,18 @@
}
int datasetCount = (datasetList == null) ? 0 : datasetList.size();
- mFillResponseEventLogger.maybeSetTotalDatasetsProvided(datasetCount);
- // It's possible that this maybe overwritten later on after PCC filtering.
- mFillResponseEventLogger.maybeSetAvailableCount(datasetCount);
+ synchronized (mLock) {
+ mFillResponseEventLogger.maybeSetTotalDatasetsProvided(datasetCount);
+ // It's possible that this maybe overwritten later on after PCC filtering.
+ mFillResponseEventLogger.maybeSetAvailableCount(datasetCount);
- // TODO(b/266379948): Ideally wait for PCC request to finish for a while more
- // (say 100ms) before proceeding further on.
+ // TODO(b/266379948): Ideally wait for PCC request to finish for a while more
+ // (say 100ms) before proceeding further on.
- processResponseLockedForPcc(response, response.getClientState(), requestFlags);
- mFillResponseEventLogger.maybeSetLatencyResponseProcessingMillis();
- mFillResponseEventLogger.logAndEndEvent();
+ processResponseLockedForPcc(response, response.getClientState(), requestFlags);
+ mFillResponseEventLogger.maybeSetLatencyResponseProcessingMillis();
+ mFillResponseEventLogger.logAndEndEvent();
+ }
}
@@ -2381,21 +2382,22 @@
@Nullable CharSequence message) {
boolean showMessage = !TextUtils.isEmpty(message);
- // Start a new FillResponse logger for the failure or timeout case.
- mFillResponseEventLogger.startLogForNewResponse();
- mFillResponseEventLogger.maybeSetRequestId(requestId);
- mFillResponseEventLogger.maybeSetAppPackageUid(uid);
- mFillResponseEventLogger.maybeSetAvailableCount(
- AVAILABLE_COUNT_WHEN_FILL_REQUEST_FAILED_OR_TIMEOUT);
- mFillResponseEventLogger.maybeSetTotalDatasetsProvided(
- AVAILABLE_COUNT_WHEN_FILL_REQUEST_FAILED_OR_TIMEOUT);
- mFillResponseEventLogger.maybeSetDetectionPreference(getDetectionPreferenceForLogging());
- final long fillRequestReceivedRelativeTimestamp =
- SystemClock.elapsedRealtime() - mLatencyBaseTime;
- mFillResponseEventLogger.maybeSetLatencyFillResponseReceivedMillis(
- (int)(fillRequestReceivedRelativeTimestamp));
-
synchronized (mLock) {
+ // Start a new FillResponse logger for the failure or timeout case.
+ mFillResponseEventLogger.startLogForNewResponse();
+ mFillResponseEventLogger.maybeSetRequestId(requestId);
+ mFillResponseEventLogger.maybeSetAppPackageUid(uid);
+ mFillResponseEventLogger.maybeSetAvailableCount(
+ AVAILABLE_COUNT_WHEN_FILL_REQUEST_FAILED_OR_TIMEOUT);
+ mFillResponseEventLogger.maybeSetTotalDatasetsProvided(
+ AVAILABLE_COUNT_WHEN_FILL_REQUEST_FAILED_OR_TIMEOUT);
+ mFillResponseEventLogger.maybeSetDetectionPreference(
+ getDetectionPreferenceForLogging());
+ final long fillRequestReceivedRelativeTimestamp =
+ SystemClock.elapsedRealtime() - mLatencyBaseTime;
+ mFillResponseEventLogger.maybeSetLatencyFillResponseReceivedMillis(
+ (int) (fillRequestReceivedRelativeTimestamp));
+
unregisterDelayedFillBroadcastLocked();
if (mDestroyed) {
Slog.w(TAG, "Call to Session#onFillRequestFailureOrTimeout(req=" + requestId
@@ -2635,8 +2637,8 @@
+ id + " destroyed");
return;
}
+ mSaveEventLogger.maybeSetLatencySaveRequestMillis();
}
- mSaveEventLogger.maybeSetLatencySaveRequestMillis();
mHandler.sendMessage(obtainMessage(
AutofillManagerServiceImpl::handleSessionSave,
mService, this));
@@ -3215,6 +3217,58 @@
return saveInfo == null ? 0 : saveInfo.getFlags();
}
+ static class SaveInfoStats {
+ public int saveInfoCount;
+ public int saveDataTypeCount;
+ }
+
+ /**
+ * Get statistic information of save info in current session. Specifically
+ * 1. how many save info the current session has.
+ * 2. How many distinct save data types current session has.
+ *
+ * @return SaveInfoStats returns the above two number in a SaveInfoStats object
+ */
+ @GuardedBy("mLock")
+ private SaveInfoStats getSaveInfoStatsLocked() {
+ SaveInfoStats retSaveInfoStats = new SaveInfoStats();
+ retSaveInfoStats.saveInfoCount = -1;
+ retSaveInfoStats.saveDataTypeCount = -1;
+
+ if (mContexts == null) {
+ if (sVerbose) {
+ Slog.v(TAG, "getSaveInfoStatsLocked(): mContexts is null");
+ }
+ } else if (mResponses == null) {
+ // Happens when the activity / session was finished before the service replied, or
+ // when the service cannot autofill it (and returned a null response).
+ if (sVerbose) {
+ Slog.v(TAG, "getSaveInfoStatsLocked(): mResponses is null");
+ }
+ return retSaveInfoStats;
+ } else {
+ int numSaveInfos = 0;
+ int numSaveDataTypes = 0;
+ ArraySet<Integer> saveDataTypeSeen = new ArraySet<>();
+ final int numResponses = mResponses.size();
+ for (int responseNum = 0; responseNum < numResponses; responseNum++) {
+ final FillResponse response = mResponses.valueAt(responseNum);
+ if (response != null && response.getSaveInfo() != null) {
+ numSaveInfos += 1;
+ int saveDataType = response.getSaveInfo().getType();
+ if (!saveDataTypeSeen.contains(saveDataType)) {
+ saveDataTypeSeen.add(saveDataType);
+ numSaveDataTypes += 1;
+ }
+ }
+ }
+ retSaveInfoStats.saveInfoCount = numSaveInfos;
+ retSaveInfoStats.saveDataTypeCount = numSaveDataTypes;
+ }
+
+ return retSaveInfoStats;
+ }
+
/**
* Generates a {@link android.service.autofill.FillEventHistory.Event#TYPE_CONTEXT_COMMITTED}
* when necessary.
@@ -3227,7 +3281,9 @@
mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this,
Event.NO_SAVE_UI_REASON_NONE,
COMMIT_REASON_UNKNOWN));
- logAllEvents(COMMIT_REASON_UNKNOWN);
+ synchronized (mLock) {
+ logAllEventsLocked(COMMIT_REASON_UNKNOWN);
+ }
}
/**
@@ -3251,6 +3307,11 @@
mSessionCommittedEventLogger.maybeSetCommitReason(commitReason);
mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount);
+ SaveInfoStats saveInfoStats = getSaveInfoStatsLocked();
+ mSessionCommittedEventLogger.maybeSetSaveInfoCount(saveInfoStats.saveInfoCount);
+ mSessionCommittedEventLogger.maybeSetSaveDataTypeCount(saveInfoStats.saveDataTypeCount);
+ mSessionCommittedEventLogger.maybeSetLastFillResponseHasSaveInfo(
+ getSaveInfoLocked() != null);
mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_NONE);
}
@@ -4571,7 +4632,7 @@
FillResponse response = viewState.getSecondaryResponse();
if (response != null) {
- logPresentationStatsOnViewEntered(response, isCredmanRequested);
+ logPresentationStatsOnViewEnteredLocked(response, isCredmanRequested);
}
// If the ViewState is ready to be displayed, onReady() will be called.
@@ -4662,7 +4723,7 @@
FillResponse response = viewState.getResponse();
if (response != null) {
- logPresentationStatsOnViewEntered(response, isCredmanRequested);
+ logPresentationStatsOnViewEnteredLocked(response, isCredmanRequested);
}
if (isSameViewEntered) {
@@ -4703,7 +4764,7 @@
}
@GuardedBy("mLock")
- private void logPresentationStatsOnViewEntered(FillResponse response,
+ private void logPresentationStatsOnViewEnteredLocked(FillResponse response,
boolean isCredmanRequested) {
mPresentationStatsEventLogger.maybeSetRequestId(response.getRequestId());
mPresentationStatsEventLogger.maybeSetIsCredentialRequest(isCredmanRequested);
@@ -5061,7 +5122,7 @@
getUiForShowing().showFillDialog(filledId, response, filterText,
mService.getServicePackageName(), mComponentName, serviceIcon, this,
- id, mCompatMode, mPresentationStatsEventLogger);
+ id, mCompatMode, mPresentationStatsEventLogger, mLock);
return true;
}
@@ -5380,9 +5441,10 @@
try {
if (sVerbose) {
- Slog.v(TAG, "updateTrackedIdsLocked(): " + trackedViews + " => " + fillableIds
- + " triggerId: " + saveTriggerId + " saveOnFinish:" + saveOnFinish
- + " flags: " + flags + " hasSaveInfo: " + (saveInfo != null));
+ Slog.v(TAG, "updateTrackedIdsLocked(): trackedViews: " + trackedViews
+ + " fillableIds: " + fillableIds + " triggerId: " + saveTriggerId
+ + " saveOnFinish:" + saveOnFinish + " flags: " + flags
+ + " hasSaveInfo: " + (saveInfo != null));
}
mClient.setTrackedViews(id, toArray(trackedViews), mSaveOnAllViewsInvisible,
saveOnFinish, toArray(fillableIds), saveTriggerId);
@@ -5420,7 +5482,7 @@
* Sets the state of views that failed to autofill.
*/
@GuardedBy("mLock")
- void setViewAutofilled(@NonNull AutofillId id) {
+ void setViewAutofilledLocked(@NonNull AutofillId id) {
if (sVerbose) {
Slog.v(TAG, "View autofilled: " + id);
}
@@ -6603,6 +6665,8 @@
boolean waitingDatasetAuth = false;
boolean hideHighlight = (entryCount == 1
&& dataset.getFieldIds().get(0).equals(mCurrentViewId));
+ // Count how many views are filtered because they are not in current session
+ int numOfViewsFiltered = 0;
for (int i = 0; i < entryCount; i++) {
if (dataset.getFieldValues().get(i) == null) {
continue;
@@ -6615,6 +6679,7 @@
Slog.v(TAG, "Skipping filling view: " +
viewId + " as it isn't part of the current session: " + id);
}
+ numOfViewsFiltered += 1;
continue;
}
ids.add(viewId);
@@ -6628,6 +6693,8 @@
viewState.resetState(ViewState.STATE_WAITING_DATASET_AUTH);
}
}
+ mPresentationStatsEventLogger.maybeSetFilteredFillableViewsCount(
+ numOfViewsFiltered);
if (!ids.isEmpty()) {
if (waitingDatasetAuth) {
mUi.hideFillUi(this);
@@ -6663,7 +6730,7 @@
}
@GuardedBy("mLock")
- private void logAllEvents(@AutofillCommitReason int val) {
+ private void logAllEventsLocked(@AutofillCommitReason int val) {
if (sVerbose) {
Slog.v(TAG, "logAllEvents(" + id + "): commitReason: " + val);
}
@@ -6695,7 +6762,7 @@
if (sVerbose) {
Slog.v(TAG, "destroyLocked for session: " + id);
}
- logAllEvents(COMMIT_REASON_SESSION_DESTROYED);
+ logAllEventsLocked(COMMIT_REASON_SESSION_DESTROYED);
if (mDestroyed) {
return null;
diff --git a/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java b/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java
index 1be8548..8f3c880 100644
--- a/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java
@@ -94,6 +94,33 @@
}
/**
+ * Set how many save infos there are in current session as long as mEventInternal presents.
+ */
+ public void maybeSetSaveInfoCount(int saveInfoCount) {
+ mEventInternal.ifPresent(event -> {
+ event.mSaveInfoCount = saveInfoCount;
+ });
+ }
+
+ /**
+ * Set how many save data types there are in current session as long as mEventInternal presents.
+ */
+ public void maybeSetSaveDataTypeCount(int saveDataTypeCount) {
+ mEventInternal.ifPresent(event -> {
+ event.mSaveDataTypeCount = saveDataTypeCount;
+ });
+ }
+
+ /**
+ * Set whether last fill response in session has save info as long as mEventInternal presents.
+ */
+ public void maybeSetLastFillResponseHasSaveInfo(boolean lastFillResponseHasSaveInfo) {
+ mEventInternal.ifPresent(event -> {
+ event.mLastFillResponseHasSaveInfo = lastFillResponseHasSaveInfo;
+ });
+ }
+
+ /**
* Log an AUTOFILL_SESSION_COMMITTED event.
*/
public void logAndEndEvent() {
@@ -109,7 +136,10 @@
+ " mRequestCount=" + event.mRequestCount
+ " mCommitReason=" + event.mCommitReason
+ " mSessionDurationMillis=" + event.mSessionDurationMillis
- + " mServiceUid=" + event.mServiceUid);
+ + " mServiceUid=" + event.mServiceUid
+ + " mSaveInfoCount=" + event.mSaveInfoCount
+ + " mSaveDataTypeCount=" + event.mSaveDataTypeCount
+ + " mLastFillResponseHasSaveInfo=" + event.mLastFillResponseHasSaveInfo);
}
FrameworkStatsLog.write(
AUTOFILL_SESSION_COMMITTED,
@@ -118,7 +148,10 @@
event.mRequestCount,
event.mCommitReason,
event.mSessionDurationMillis,
- event.mServiceUid);
+ event.mServiceUid,
+ event.mSaveInfoCount,
+ event.mSaveDataTypeCount,
+ event.mLastFillResponseHasSaveInfo);
mEventInternal = Optional.empty();
}
@@ -127,6 +160,9 @@
int mRequestCount = 0;
int mCommitReason = COMMIT_REASON_UNKNOWN;
long mSessionDurationMillis = 0;
+ int mSaveInfoCount = -1;
+ int mSaveDataTypeCount = -1;
+ boolean mLastFillResponseHasSaveInfo = false;
int mServiceUid = -1;
SessionCommittedEventInternal() {
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index 3b9c54f..8cc666b 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -425,7 +425,8 @@
@Nullable String filterText, @Nullable String servicePackageName,
@NonNull ComponentName componentName, @Nullable Drawable serviceIcon,
@NonNull AutoFillUiCallback callback, int sessionId, boolean compatMode,
- @Nullable PresentationStatsEventLogger mPresentationStatsEventLogger) {
+ @Nullable PresentationStatsEventLogger presentationStatsEventLogger,
+ @NonNull Object sessionLock) {
if (sVerbose) {
Slog.v(TAG, "showFillDialog for "
+ componentName.toShortString() + ": " + response);
@@ -467,9 +468,11 @@
@Override
public void onDatasetPicked(Dataset dataset) {
log(MetricsEvent.TYPE_ACTION);
- if (mPresentationStatsEventLogger != null) {
- mPresentationStatsEventLogger.maybeSetPositiveCtaButtonClicked(
- true);
+ synchronized (sessionLock) {
+ if (presentationStatsEventLogger != null) {
+ presentationStatsEventLogger.maybeSetPositiveCtaButtonClicked(
+ true);
+ }
}
hideFillDialogUiThread(callback);
if (mCallback != null) {
@@ -482,8 +485,10 @@
@Override
public void onDismissed() {
log(MetricsEvent.TYPE_DISMISS);
- if (mPresentationStatsEventLogger != null) {
- mPresentationStatsEventLogger.maybeSetDialogDismissed(true);
+ synchronized (sessionLock) {
+ if (presentationStatsEventLogger != null) {
+ presentationStatsEventLogger.maybeSetDialogDismissed(true);
+ }
}
hideFillDialogUiThread(callback);
callback.requestShowSoftInput(focusedId);
@@ -493,9 +498,11 @@
@Override
public void onCanceled() {
log(MetricsEvent.TYPE_CLOSE);
- if (mPresentationStatsEventLogger != null) {
- mPresentationStatsEventLogger.maybeSetNegativeCtaButtonClicked(
- true);
+ synchronized (sessionLock) {
+ if (presentationStatsEventLogger != null) {
+ presentationStatsEventLogger.maybeSetNegativeCtaButtonClicked(
+ true);
+ }
}
hideFillDialogUiThread(callback);
callback.requestShowSoftInput(focusedId);
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 78edb8e..1831ecd 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -689,12 +689,20 @@
Slog.v(TAG, "AutofillWindowPresenter.show(): fit=" + fitsSystemWindows
+ ", params=" + paramsToString(p));
}
- UiThread.getHandler().post(() -> mWindow.show(p));
+ UiThread.getHandler().post(() -> {
+ if (mWindow != null) {
+ mWindow.show(p);
+ }
+ });
}
@Override
public void hide(Rect transitionEpicenter) {
- UiThread.getHandler().post(mWindow::hide);
+ UiThread.getHandler().post(() -> {
+ if (mWindow != null) {
+ mWindow.hide();
+ }
+ });
}
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index c9cce15..0ab6bbc 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -532,7 +532,8 @@
String packageName, int userId) {
startObservingDevicePresence_enforcePermission();
- mDevicePresenceProcessor.startObservingDevicePresence(request, packageName, userId);
+ mDevicePresenceProcessor.startObservingDevicePresence(
+ request, packageName, userId, /* enforcePermissions */ true);
}
@Override
@@ -541,7 +542,8 @@
String packageName, int userId) {
stopObservingDevicePresence_enforcePermission();
- mDevicePresenceProcessor.stopObservingDevicePresence(request, packageName, userId);
+ mDevicePresenceProcessor.stopObservingDevicePresence(
+ request, packageName, userId, /* enforcePermissions */ true);
}
@Override
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index c892b84..3d53deb 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -21,6 +21,7 @@
import android.companion.AssociationInfo;
import android.companion.ContextSyncMessage;
import android.companion.Flags;
+import android.companion.ObservingDevicePresenceRequest;
import android.companion.Telecom;
import android.companion.datatransfer.PermissionSyncRequest;
import android.net.MacAddress;
@@ -193,6 +194,43 @@
break;
}
+ case "start-observing-device-presence-uuid": {
+ if (Flags.devicePresence()) {
+ int userId = getNextIntArgRequired();
+ String packageName = getNextArgRequired();
+ String uuid = getNextArgRequired();
+ if ("null".equals(uuid)) {
+ out.println("UUID can not be null.");
+ break;
+ }
+ ParcelUuid parcelUuid = ParcelUuid.fromString(uuid);
+ ObservingDevicePresenceRequest request = new ObservingDevicePresenceRequest
+ .Builder().setUuid(parcelUuid).build();
+ mDevicePresenceProcessor.startObservingDevicePresence(
+ request, packageName, userId, /* enforcePermissions */ false);
+
+ }
+ break;
+ }
+
+ case "stop-observing-device-presence-uuid": {
+ if (Flags.devicePresence()) {
+ int userId = getNextIntArgRequired();
+ String packageName = getNextArgRequired();
+ String uuid = getNextArgRequired();
+ if ("null".equals(uuid)) {
+ out.println("UUID can not be null.");
+ break;
+ }
+ ParcelUuid parcelUuid = ParcelUuid.fromString(uuid);
+ ObservingDevicePresenceRequest request = new ObservingDevicePresenceRequest
+ .Builder().setUuid(parcelUuid).build();
+ mDevicePresenceProcessor.stopObservingDevicePresence(
+ request, packageName, userId, /* enforcePermissions */ false);
+ }
+ break;
+ }
+
case "get-backup-payload": {
final int userId = getNextIntArgRequired();
byte[] payload = mBackupRestoreProcessor.getBackupPayload(userId);
@@ -515,6 +553,14 @@
pw.println(" callback after simulate-device-event-device-locked");
pw.println(" command has been called.");
pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY.");
+
+ pw.println(" start-observing-device-presence-uuid USER_ID PACKAGE_NAME UUID");
+ pw.println(" Start observing device presence base on the UUID.");
+ pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY.");
+
+ pw.println(" stop-observing-device-presence-uuid USER_ID PACKAGE_NAME UUID");
+ pw.println(" Stop observing device presence base on the UUID.");
+ pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY.");
}
pw.println(" remove-inactive-associations");
diff --git a/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java b/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java
index af49df6..a374d27 100644
--- a/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java
+++ b/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java
@@ -181,14 +181,16 @@
* Process device presence start request.
*/
public void startObservingDevicePresence(ObservingDevicePresenceRequest request,
- String packageName, int userId) {
+ String packageName, int userId, boolean enforcePermissions) {
Slog.i(TAG,
"Start observing request=[" + request + "] for userId=[" + userId + "], package=["
+ packageName + "]...");
final ParcelUuid requestUuid = request.getUuid();
if (requestUuid != null) {
- enforceCallerCanObserveDevicePresenceByUuid(mContext);
+ if (enforcePermissions) {
+ enforceCallerCanObserveDevicePresenceByUuid(mContext, packageName, userId);
+ }
// If it's already being observed, then no-op.
if (mObservableUuidStore.isUuidBeingObserved(requestUuid, userId, packageName)) {
@@ -236,7 +238,7 @@
* Process device presence stop request.
*/
public void stopObservingDevicePresence(ObservingDevicePresenceRequest request,
- String packageName, int userId) {
+ String packageName, int userId, boolean enforcePermissions) {
Slog.i(TAG,
"Stop observing request=[" + request + "] for userId=[" + userId + "], package=["
+ packageName + "]...");
@@ -244,7 +246,9 @@
final ParcelUuid requestUuid = request.getUuid();
if (requestUuid != null) {
- enforceCallerCanObserveDevicePresenceByUuid(mContext);
+ if (enforcePermissions) {
+ enforceCallerCanObserveDevicePresenceByUuid(mContext, packageName, userId);
+ }
if (!mObservableUuidStore.isUuidBeingObserved(requestUuid, userId, packageName)) {
Slog.i(TAG, "UUID=[" + requestUuid + "], package=[" + packageName + "], userId=["
@@ -283,7 +287,7 @@
* For legacy device presence below Android V.
*
* @deprecated Use {@link #startObservingDevicePresence(ObservingDevicePresenceRequest, String,
- * int)}
+ * int, boolean)}
*/
@Deprecated
public void startObservingDevicePresence(int userId, String packageName, String deviceAddress)
@@ -306,14 +310,14 @@
startObservingDevicePresence(
new ObservingDevicePresenceRequest.Builder().setAssociationId(association.getId())
- .build(), packageName, userId);
+ .build(), packageName, userId, /* enforcePermissions */ true);
}
/**
* For legacy device presence below Android V.
*
* @deprecated Use {@link #stopObservingDevicePresence(ObservingDevicePresenceRequest, String,
- * int)}
+ * int, boolean)}
*/
@Deprecated
public void stopObservingDevicePresence(int userId, String packageName, String deviceAddress)
@@ -336,7 +340,7 @@
stopObservingDevicePresence(
new ObservingDevicePresenceRequest.Builder().setAssociationId(association.getId())
- .build(), packageName, userId);
+ .build(), packageName, userId, /* enforcePermissions */ true);
}
/**
diff --git a/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
index 796d285..c927cd0 100644
--- a/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
+++ b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
@@ -35,6 +35,8 @@
import static android.os.Process.SYSTEM_UID;
import static android.os.UserHandle.getCallingUserId;
+import static com.android.server.companion.utils.RolesUtils.isRoleHolder;
+
import static java.util.Collections.unmodifiableMap;
import android.Manifest;
@@ -44,6 +46,7 @@
import android.companion.AssociationRequest;
import android.companion.CompanionDeviceManager;
import android.content.Context;
+import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.ArrayMap;
@@ -203,11 +206,9 @@
/**
* Require the caller to hold necessary permission to observe device presence by UUID.
*/
- public static void enforceCallerCanObserveDevicePresenceByUuid(@NonNull Context context) {
- if (context.checkCallingPermission(REQUEST_OBSERVE_DEVICE_UUID_PRESENCE)
- != PERMISSION_GRANTED
- || context.checkCallingPermission(BLUETOOTH_SCAN) != PERMISSION_GRANTED
- || context.checkCallingPermission(BLUETOOTH_CONNECT) != PERMISSION_GRANTED) {
+ public static void enforceCallerCanObserveDevicePresenceByUuid(@NonNull Context context,
+ String packageName, int userId) {
+ if (!hasRequirePermissions(context, packageName, userId)) {
throw new SecurityException("Caller (uid=" + getCallingUid() + ") does not have "
+ "permissions to request observing device presence base on the UUID");
}
@@ -234,6 +235,17 @@
return sAppOpsService;
}
+ private static boolean hasRequirePermissions(
+ @NonNull Context context, String packageName, int userId) {
+ return context.checkCallingPermission(
+ REQUEST_OBSERVE_DEVICE_UUID_PRESENCE) == PERMISSION_GRANTED
+ && context.checkCallingPermission(BLUETOOTH_SCAN) == PERMISSION_GRANTED
+ && context.checkCallingPermission(BLUETOOTH_CONNECT) == PERMISSION_GRANTED
+ && Boolean.TRUE.equals(Binder.withCleanCallingIdentity(
+ () -> isRoleHolder(context, userId, packageName,
+ DEVICE_PROFILE_AUTOMOTIVE_PROJECTION)));
+ }
+
// DO NOT USE DIRECTLY! Access via getAppOpsService().
private static IAppOpsService sAppOpsService = null;
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 2607ed3..89c888c 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -18,6 +18,7 @@
import static android.Manifest.permission.MANAGE_CONTENT_CAPTURE;
import static android.content.Context.CONTENT_CAPTURE_MANAGER_SERVICE;
+import static android.service.contentcapture.ContentCaptureService.ASSIST_CONTENT_ACTIVITY_START_KEY;
import static android.service.contentcapture.ContentCaptureService.setClientState;
import static android.view.contentcapture.ContentCaptureHelper.toList;
import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_ALLOWLIST_DELAY_MS;
@@ -28,6 +29,7 @@
import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_OPTIONAL_GROUPS_THRESHOLD;
import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_REQUIRED_GROUPS_CONFIG;
import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_ENABLE_CONTENT_PROTECTION_RECEIVER;
+import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_ENABLE_ACTIVITY_START_ASSIST_CONTENT;
import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_FALSE;
import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_OK;
import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_SECURITY_EXCEPTION;
@@ -44,6 +46,7 @@
import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_WRITE_FINISHED;
import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__REJECT_DATA_SHARE_REQUEST;
import static com.android.internal.util.SyncResultReceiver.bundleFor;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -52,10 +55,12 @@
import android.app.ActivityThread;
import android.app.admin.DevicePolicyCache;
import android.app.assist.ActivityId;
+import android.app.assist.AssistContent;
import android.content.ComponentName;
import android.content.ContentCaptureOptions;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.ActivityPresentationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -184,6 +189,9 @@
@Nullable
private boolean mDisabledByDeviceConfig;
+ @GuardedBy("mLock")
+ private boolean activityStartAssistDataEnabled;
+
// Device-config settings that are cached and passed back to apps
@GuardedBy("mLock")
int mDevCfgLoggingLevel;
@@ -451,6 +459,8 @@
case DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_AUTO_DISCONNECT_TIMEOUT:
setFineTuneParamsFromDeviceConfig();
return;
+ case DEVICE_CONFIG_ENABLE_ACTIVITY_START_ASSIST_CONTENT:
+ setActivityStartAssistDataEnabled();
default:
Slog.i(TAG, "Ignoring change on " + key);
}
@@ -639,6 +649,15 @@
final String enabled = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED);
setDisabledByDeviceConfig(enabled);
+ setActivityStartAssistDataEnabled();
+ }
+
+ private void setActivityStartAssistDataEnabled() {
+ synchronized (mLock) {
+ this.activityStartAssistDataEnabled = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
+ DEVICE_CONFIG_ENABLE_ACTIVITY_START_ASSIST_CONTENT, false);
+ }
}
private void setDisabledByDeviceConfig(@Nullable String explicitlyEnabled) {
@@ -908,6 +927,9 @@
pw.print(prefix2);
pw.print("contentProtectionAutoDisconnectTimeoutMs: ");
pw.println(mDevCfgContentProtectionAutoDisconnectTimeoutMs);
+ pw.print(prefix2);
+ pw.print("activityStartAssistDataEnabled: ");
+ pw.println(activityStartAssistDataEnabled);
pw.print(prefix);
pw.println("Global Options:");
mGlobalContentCaptureOptions.dump(prefix2, pw);
@@ -1327,6 +1349,33 @@
}
@Override
+ @SuppressWarnings("GuardedBy")
+ public boolean sendActivityStartAssistData(@UserIdInt int userId,
+ @NonNull IBinder activityToken,
+ @NonNull Intent intentData) {
+ synchronized (mLock) {
+ if (!activityStartAssistDataEnabled) {
+ return false;
+ }
+ Intent intent = new Intent(intentData);
+ intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION));
+ Bundle assistContentExtras = new Bundle();
+ assistContentExtras.putBoolean(ASSIST_CONTENT_ACTIVITY_START_KEY, true);
+ AssistContent assistContent = new AssistContent(assistContentExtras);
+ assistContent.setDefaultIntent(intent);
+
+ final Bundle activityAssistData = new Bundle();
+ activityAssistData.putParcelable(ASSIST_KEY_CONTENT, assistContent);
+ final ContentCapturePerUserService service = peekServiceForUserLocked(userId);
+ if (service != null) {
+ return service.sendActivityAssistDataLocked(activityToken, activityAssistData);
+ }
+ }
+ return false;
+ }
+
+ @Override
public boolean sendActivityAssistData(@UserIdInt int userId, @NonNull IBinder activityToken,
@NonNull Bundle data) {
synchronized (mLock) {
diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
index bc35fea..38bbfc4 100644
--- a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
+++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
@@ -47,6 +47,7 @@
import android.app.contextualsearch.ContextualSearchState;
import android.app.contextualsearch.IContextualSearchCallback;
import android.app.contextualsearch.IContextualSearchManager;
+import android.app.contextualsearch.flags.Flags;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -91,6 +92,8 @@
private static final String TAG = ContextualSearchManagerService.class.getSimpleName();
private static final int MSG_RESET_TEMPORARY_PACKAGE = 0;
private static final int MAX_TEMP_PACKAGE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
+ private static final int MSG_INVALIDATE_TOKEN = 1;
+ private static final int MAX_TOKEN_VALID_DURATION_MS = 1_000 * 60 * 10; // 10 minutes
private final Context mContext;
private final ActivityTaskManagerInternal mAtmInternal;
@@ -145,6 +148,8 @@
private Handler mTemporaryHandler;
@GuardedBy("this")
private String mTemporaryPackage = null;
+ @GuardedBy("this")
+ private long mTokenValidDurationMs = MAX_TOKEN_VALID_DURATION_MS;
@GuardedBy("mLock")
private IContextualSearchCallback mStateCallback;
@@ -212,6 +217,29 @@
}
}
+ void resetTokenValidDurationMs() {
+ setTokenValidDurationMs(MAX_TOKEN_VALID_DURATION_MS);
+ }
+
+ void setTokenValidDurationMs(int durationMs) {
+ synchronized (this) {
+ enforceOverridingPermission("setTokenValidDurationMs");
+ if (durationMs > MAX_TOKEN_VALID_DURATION_MS) {
+ throw new IllegalArgumentException(
+ "Token max duration is " + MAX_TOKEN_VALID_DURATION_MS + " (called with "
+ + durationMs + ")");
+ }
+ mTokenValidDurationMs = durationMs;
+ if (DEBUG_USER) Log.d(TAG, "mTokenValidDurationMs set to " + durationMs);
+ }
+ }
+
+ private long getTokenValidDurationMs() {
+ synchronized (this) {
+ return mTokenValidDurationMs;
+ }
+ }
+
private Intent getResolvedLaunchIntent() {
synchronized (this) {
// If mTemporaryPackage is not null, use it to get the ContextualSearch intent.
@@ -356,15 +384,51 @@
}
private class ContextualSearchManagerStub extends IContextualSearchManager.Stub {
+ @GuardedBy("this")
+ private Handler mTokenHandler;
private @Nullable CallbackToken mToken;
+ private void invalidateToken() {
+ synchronized (this) {
+ if (mTokenHandler != null) {
+ mTokenHandler.removeMessages(MSG_INVALIDATE_TOKEN);
+ mTokenHandler = null;
+ }
+ if (DEBUG_USER) Log.d(TAG, "mToken invalidated.");
+ mToken = null;
+ }
+ }
+
+ private void issueToken() {
+ synchronized (this) {
+ mToken = new CallbackToken();
+ if (mTokenHandler == null) {
+ mTokenHandler = new Handler(Looper.getMainLooper(), null, true) {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_INVALIDATE_TOKEN) {
+ invalidateToken();
+ } else {
+ Slog.wtf(TAG, "invalid token handler msg: " + msg);
+ }
+ }
+ };
+ } else {
+ mTokenHandler.removeMessages(MSG_INVALIDATE_TOKEN);
+ }
+ mTokenHandler.sendEmptyMessageDelayed(
+ MSG_INVALIDATE_TOKEN, getTokenValidDurationMs());
+ }
+ }
+
@Override
public void startContextualSearch(int entrypoint) {
synchronized (this) {
if (DEBUG_USER) Log.d(TAG, "startContextualSearch");
enforcePermission("startContextualSearch");
mAssistDataRequester.cancel();
- mToken = new CallbackToken();
+ // Creates a new CallbackToken at mToken and an expiration handler.
+ issueToken();
// We get the launch intent with the system server's identity because the system
// server has READ_FRAME_BUFFER permission to get the screenshot and because only
// the system server can invoke non-exported activities.
@@ -397,7 +461,18 @@
}
return;
}
- mToken = null;
+ invalidateToken();
+ if (Flags.enableTokenRefresh()) {
+ issueToken();
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(ContextualSearchManager.EXTRA_TOKEN, mToken);
+ try {
+ callback.onResult(
+ new ContextualSearchState(null, null, bundle));
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error invoking ContextualSearchCallback", e);
+ }
+ }
synchronized (mLock) {
mStateCallback = callback;
}
diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerShellCommand.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerShellCommand.java
index 5777e1d..66a4e7b 100644
--- a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerShellCommand.java
+++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerShellCommand.java
@@ -52,6 +52,19 @@
+ packageName + " for " + duration + "ms");
break;
}
+ case "token-duration": {
+ String durationStr = getNextArg();
+ if (durationStr == null) {
+ mService.resetTokenValidDurationMs();
+ pw.println("ContextualSearchManagerService token duration reset.");
+ return 0;
+ }
+ final int durationMs = Integer.parseInt(durationStr);
+ mService.setTokenValidDurationMs(durationMs);
+ pw.println("ContextualSearchManagerService temporarily set token duration"
+ + " to " + durationMs + "ms");
+ break;
+ }
}
}
break;
@@ -72,6 +85,9 @@
pw.println(" Temporarily (for DURATION ms) changes the Contextual Search "
+ "implementation.");
pw.println(" To reset, call without any arguments.");
+ pw.println(" set token-duration [DURATION]");
+ pw.println(" Changes the Contextual Search token duration to DURATION ms.");
+ pw.println(" To reset, call without any arguments.");
pw.println("");
}
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 167c384..53730e3 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -227,6 +227,7 @@
"connectivity_flags_lib",
"dreams_flags_lib",
"aconfig_new_storage_flags_lib",
+ "powerstats_flags_lib",
],
javac_shard_size: 50,
javacflags: [
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index 966478e..eb03709 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -663,6 +663,7 @@
PackageHealthObserverImpact.USER_IMPACT_LEVEL_10,
PackageHealthObserverImpact.USER_IMPACT_LEVEL_20,
PackageHealthObserverImpact.USER_IMPACT_LEVEL_30,
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_40,
PackageHealthObserverImpact.USER_IMPACT_LEVEL_50,
PackageHealthObserverImpact.USER_IMPACT_LEVEL_70,
PackageHealthObserverImpact.USER_IMPACT_LEVEL_71,
@@ -678,6 +679,7 @@
/* Actions having medium user impact, user of a device will likely notice. */
int USER_IMPACT_LEVEL_20 = 20;
int USER_IMPACT_LEVEL_30 = 30;
+ int USER_IMPACT_LEVEL_40 = 40;
int USER_IMPACT_LEVEL_50 = 50;
int USER_IMPACT_LEVEL_70 = 70;
/* Action has high user impact, a last resort, user of a device will be very frustrated. */
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index 37c2d26..189b2495 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -673,7 +673,7 @@
case RESCUE_LEVEL_SCOPED_DEVICE_CONFIG_RESET:
return PackageHealthObserverImpact.USER_IMPACT_LEVEL_10;
case RESCUE_LEVEL_ALL_DEVICE_CONFIG_RESET:
- return PackageHealthObserverImpact.USER_IMPACT_LEVEL_20;
+ return PackageHealthObserverImpact.USER_IMPACT_LEVEL_40;
case RESCUE_LEVEL_WARM_REBOOT:
return PackageHealthObserverImpact.USER_IMPACT_LEVEL_50;
case RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS:
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 0c1d0fb..e424ffa 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -3892,9 +3892,10 @@
return;
}
- final long lastTopTime = sr.app.mState.getLastTopTime();
- final long constantTimeLimit = getTimeLimitForFgsType(fgsType);
+ final boolean currentlyTop = sr.app.mState.getCurProcState() <= PROCESS_STATE_TOP;
final long nowUptime = SystemClock.uptimeMillis();
+ final long lastTopTime = currentlyTop ? nowUptime : sr.app.mState.getLastTopTime();
+ final long constantTimeLimit = getTimeLimitForFgsType(fgsType);
if (lastTopTime != Long.MIN_VALUE && constantTimeLimit > (nowUptime - lastTopTime)) {
// Discard any other messages for this service
mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_FGS_TIMEOUT_MSG, sr);
@@ -6290,7 +6291,7 @@
final ComponentName clientSideComponentName =
cr.aliasComponent != null ? cr.aliasComponent : r.name;
try {
- cr.conn.connected(r.name, null, true);
+ cr.conn.connected(clientSideComponentName, null, true);
} catch (Exception e) {
Slog.w(TAG, "Failure disconnecting service " + r.shortInstanceName
+ " to connection " + c.get(i).conn.asBinder()
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 26aa053..f5a297b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -169,6 +169,17 @@
*/
static final String KEY_ENABLE_NEW_OOMADJ = "enable_new_oom_adj";
+ /**
+ * Whether or not to enable the batching of OOM adjuster calls to LMKD
+ */
+ static final String KEY_ENABLE_BATCHING_OOM_ADJ = "enable_batching_oom_adj";
+
+ /**
+ * How long to wait before scheduling another follow-up oomAdjuster update for time based state.
+ */
+ static final String KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION =
+ "follow_up_oomadj_update_wait_duration";
+
private static final int DEFAULT_MAX_CACHED_PROCESSES = 1024;
private static final boolean DEFAULT_PRIORITIZE_ALARM_BROADCASTS = true;
private static final long DEFAULT_FGSERVICE_MIN_SHOWN_TIME = 2*1000;
@@ -231,7 +242,7 @@
static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60 * 1000;
static final long DEFAULT_KILL_BG_RESTRICTED_CACHED_IDLE_SETTLE_TIME_MS = 60 * 1000;
- static final boolean DEFAULT_KILL_BG_RESTRICTED_CACHED_IDLE = true;
+ static final boolean DEFAULT_KILL_BG_RESTRICTED_CACHED_IDLE = false;
static final int DEFAULT_MAX_SERVICE_CONNECTIONS_PER_PROCESS = 3000;
@@ -244,6 +255,16 @@
private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = Flags.oomadjusterCorrectnessRewrite();
/**
+ * The default value to {@link #KEY_ENABLE_BATCHING_OOM_ADJ}.
+ */
+ private static final boolean DEFAULT_ENABLE_BATCHING_OOM_ADJ = Flags.batchingOomAdj();
+
+ /**
+ * The default value to {@link #KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION}.
+ */
+ private static final long DEFAULT_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION = 1000L;
+
+ /**
* Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED}
*/
private static final int
@@ -1136,6 +1157,13 @@
/** @see #KEY_ENABLE_NEW_OOMADJ */
public boolean ENABLE_NEW_OOMADJ = DEFAULT_ENABLE_NEW_OOM_ADJ;
+ /** @see #KEY_ENABLE_BATCHING_OOM_ADJ */
+ public boolean ENABLE_BATCHING_OOM_ADJ = DEFAULT_ENABLE_BATCHING_OOM_ADJ;
+
+ /** @see #KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION */
+ public long FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION =
+ DEFAULT_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION;
+
/**
* Indicates whether PSS profiling in AppProfiler is disabled or not.
*/
@@ -1346,6 +1374,9 @@
case KEY_PROC_STATE_DEBUG_UIDS:
updateProcStateDebugUids();
break;
+ case KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION:
+ updateFollowUpOomAdjUpdateWaitDuration();
+ break;
default:
updateFGSPermissionEnforcementFlagsIfNecessary(name);
break;
@@ -1479,6 +1510,8 @@
private void loadNativeBootDeviceConfigConstants() {
ENABLE_NEW_OOMADJ = getDeviceConfigBoolean(KEY_ENABLE_NEW_OOMADJ,
DEFAULT_ENABLE_NEW_OOM_ADJ);
+ ENABLE_BATCHING_OOM_ADJ = getDeviceConfigBoolean(KEY_ENABLE_BATCHING_OOM_ADJ,
+ DEFAULT_ENABLE_BATCHING_OOM_ADJ);
}
public void setOverrideMaxCachedProcesses(int value) {
@@ -2231,6 +2264,13 @@
DEFAULT_ENABLE_NEW_OOM_ADJ);
}
+ private void updateFollowUpOomAdjUpdateWaitDuration() {
+ FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION = DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION,
+ DEFAULT_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION);
+ }
+
private void updateFGSPermissionEnforcementFlagsIfNecessary(@NonNull String name) {
ForegroundServiceTypePolicy.getDefaultPolicy()
.updatePermissionEnforcementFlagIfNecessary(name);
@@ -2248,6 +2288,13 @@
mDefaultPssToRssThresholdModifier);
}
+ private void updateEnableBatchingOomAdj() {
+ ENABLE_BATCHING_OOM_ADJ = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
+ KEY_ENABLE_BATCHING_OOM_ADJ,
+ DEFAULT_ENABLE_BATCHING_OOM_ADJ);
+ }
+
boolean shouldDebugUidForProcState(int uid) {
SparseBooleanArray ar = mProcStateDebugUids;
final var size = ar.size();
@@ -2476,6 +2523,9 @@
pw.print(" "); pw.print(KEY_MAX_PREVIOUS_TIME);
pw.print("="); pw.println(MAX_PREVIOUS_TIME);
+ pw.print(" "); pw.print(KEY_ENABLE_BATCHING_OOM_ADJ);
+ pw.print("="); pw.println(ENABLE_BATCHING_OOM_ADJ);
+
pw.println();
if (mOverrideMaxCachedProcesses >= 0) {
pw.print(" mOverrideMaxCachedProcesses="); pw.println(mOverrideMaxCachedProcesses);
@@ -2489,6 +2539,9 @@
pw.print(" ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION=");
pw.println(mEnableWaitForFinishAttachApplication);
+ pw.print(" "); pw.print(KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION);
+ pw.print("="); pw.println(FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION);
+
synchronized (mProcStateDebugUids) {
pw.print(" "); pw.print(KEY_PROC_STATE_DEBUG_UIDS);
pw.print("="); pw.println(mProcStateDebugUids);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2a724cd..5ffab55 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1663,6 +1663,7 @@
static final int BIND_APPLICATION_TIMEOUT_HARD_MSG = 83;
static final int SERVICE_FGS_TIMEOUT_MSG = 84;
static final int SERVICE_FGS_CRASH_TIMEOUT_MSG = 85;
+ static final int FOLLOW_UP_OOMADJUSTER_UPDATE_MSG = 86;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -2036,6 +2037,9 @@
case SERVICE_FGS_CRASH_TIMEOUT_MSG: {
mServices.onFgsCrashTimeout((ServiceRecord) msg.obj);
} break;
+ case FOLLOW_UP_OOMADJUSTER_UPDATE_MSG: {
+ handleFollowUpOomAdjusterUpdate();
+ } break;
}
}
}
@@ -3933,11 +3937,28 @@
+ packageName + ": " + e);
}
if (mUserController.isUserRunning(user, userRunningFlags)) {
+
+ String description;
+ if (reason == null) {
+ description = "from pid " + callingPid;
+
+ // Add the name of the process if it's available
+ final ProcessRecord callerApp;
+ synchronized (mPidsSelfLocked) {
+ callerApp = mPidsSelfLocked.get(callingPid);
+ }
+ if (callerApp != null) {
+ description += " (" + callerApp.processName + ")";
+ }
+ } else {
+ description = reason;
+ }
+
forceStopPackageLocked(packageName, UserHandle.getAppId(pkgUid),
false /* callerWillRestart */, false /* purgeCache */,
true /* doIt */, false /* evenPersistent */,
- false /* uninstalling */, true /* packageStateStopped */, user,
- reason == null ? ("from pid " + callingPid) : reason);
+ false /* uninstalling */, true /* packageStateStopped */,
+ user, description);
finishForceStopPackageLocked(packageName, pkgUid);
}
}
@@ -5086,6 +5107,15 @@
mAnrHelper.appNotResponding(app, TimeoutRecord.forAppStart(anrMessage));
}
+ private void handleFollowUpOomAdjusterUpdate() {
+ // Remove any existing duplicate messages on the handler here while no lock is being held.
+ // If another follow up update is needed, it will be scheduled by OomAdjuster.
+ mHandler.removeMessages(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG);
+ synchronized (this) {
+ mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
+ }
+ }
+
/**
* @return The last part of the string of an intent's action.
*/
@@ -14612,7 +14642,7 @@
final StringBuilder sb = new StringBuilder("registerReceiver: ");
sb.append(Binder.getCallingUid()); sb.append('/');
sb.append(receiverId == null ? "null" : receiverId); sb.append('/');
- final int actionsCount = filter.countActions();
+ final int actionsCount = filter.safeCountActions();
if (actionsCount > 0) {
for (int i = 0; i < actionsCount; ++i) {
sb.append(filter.getAction(i));
@@ -15274,15 +15304,50 @@
BackgroundStartPrivileges backgroundStartPrivileges,
@Nullable int[] broadcastAllowList,
@Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver) {
- final int cookie = BroadcastQueue.traceBegin("broadcastIntentLockedTraced");
- final int res = broadcastIntentLockedTraced(callerApp, callerPackage, callerFeatureId,
- intent, resolvedType, resultToApp, resultTo, resultCode, resultData, resultExtras,
- requiredPermissions, excludedPermissions, excludedPackages, appOp,
- BroadcastOptions.fromBundleNullable(bOptions), ordered, sticky,
- callingPid, callingUid, realCallingUid, realCallingPid, userId,
- backgroundStartPrivileges, broadcastAllowList, filterExtrasForReceiver);
- BroadcastQueue.traceEnd(cookie);
- return res;
+ final int cookie = traceBroadcastIntentBegin(intent, resultTo, ordered, sticky,
+ callingUid, realCallingUid, userId);
+ try {
+ final int res = broadcastIntentLockedTraced(callerApp, callerPackage, callerFeatureId,
+ intent, resolvedType, resultToApp, resultTo, resultCode, resultData,
+ resultExtras, requiredPermissions, excludedPermissions, excludedPackages,
+ appOp, BroadcastOptions.fromBundleNullable(bOptions), ordered, sticky,
+ callingPid, callingUid, realCallingUid, realCallingPid, userId,
+ backgroundStartPrivileges, broadcastAllowList, filterExtrasForReceiver);
+ return res;
+ } finally {
+ traceBroadcastIntentEnd(cookie);
+ }
+ }
+
+ private static int traceBroadcastIntentBegin(Intent intent, IIntentReceiver resultTo,
+ boolean ordered, boolean sticky, int callingUid, int realCallingUid, int userId) {
+ if (!Flags.traceReceiverRegistration()) {
+ return BroadcastQueue.traceBegin("broadcastIntentLockedTraced");
+ }
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+ final StringBuilder sb = new StringBuilder("broadcastIntent: ");
+ sb.append(callingUid); sb.append('/');
+ final String action = intent.getAction();
+ sb.append(action == null ? null : action); sb.append('/');
+ sb.append("0x"); sb.append(Integer.toHexString(intent.getFlags())); sb.append('/');
+ sb.append(ordered ? "O" : "_");
+ sb.append(sticky ? "S" : "_");
+ sb.append(resultTo != null ? "C" : "_");
+ sb.append('/');
+ sb.append('u'); sb.append(userId);
+ if (callingUid != realCallingUid) {
+ sb.append('/');
+ sb.append("sender="); sb.append(realCallingUid);
+ }
+ return BroadcastQueue.traceBegin(sb.toString());
+ }
+ return 0;
+ }
+
+ private static void traceBroadcastIntentEnd(int cookie) {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+ BroadcastQueue.traceEnd(cookie);
+ }
}
@GuardedBy("this")
diff --git a/services/core/java/com/android/server/am/AppStartInfoTracker.java b/services/core/java/com/android/server/am/AppStartInfoTracker.java
index dc6e2fa3..4a7ad31 100644
--- a/services/core/java/com/android/server/am/AppStartInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppStartInfoTracker.java
@@ -89,6 +89,14 @@
@VisibleForTesting static final int APP_START_INFO_HISTORY_LIST_SIZE = 16;
+ /**
+ * The max number of records that can be present in {@link mInProgressRecords}.
+ *
+ * The magic number of 5 records is expected to be enough because this covers in progress
+ * activity starts only, of which more than a 1-2 at a time is very uncommon/unlikely.
+ */
+ @VisibleForTesting static final int MAX_IN_PROGRESS_RECORDS = 5;
+
private static final int APP_START_INFO_MONITORING_MODE_LIST_SIZE = 100;
@VisibleForTesting static final String APP_START_STORE_DIR = "procstartstore";
@@ -147,7 +155,6 @@
/** The path to the historical proc start info file, persisted in the storage. */
@VisibleForTesting File mProcStartInfoFile;
-
/**
* Temporary list of records that have not been completed.
*
@@ -155,7 +162,12 @@
*/
@GuardedBy("mLock")
@VisibleForTesting
- final ArrayMap<Long, ApplicationStartInfo> mInProgRecords = new ArrayMap<>();
+ final ArrayMap<Long, ApplicationStartInfo> mInProgressRecords = new ArrayMap<>();
+
+ /** Temporary list of keys present in {@link mInProgressRecords} for sorting. */
+ @GuardedBy("mLock")
+ @VisibleForTesting
+ final ArrayList<Integer> mTemporaryInProgressIndexes = new ArrayList<>();
AppStartInfoTracker() {
mCallbacks = new SparseArray<>();
@@ -193,6 +205,60 @@
});
}
+ /**
+ * Trim in progress records structure to acceptable size. To be called after each time a new
+ * record is added.
+ *
+ * This is necessary both for robustness, as well as because the call to
+ * {@link onReportFullyDrawn} which triggers the removal in the success case is not guaranteed.
+ *
+ * <p class="note"> Note: this is the expected path for removal of in progress records for
+ * successful activity triggered starts that don't report fully drawn. It is *not* only an edge
+ * case.</p>
+ */
+ @GuardedBy("mLock")
+ private void maybeTrimInProgressRecordsLocked() {
+ if (mInProgressRecords.size() <= MAX_IN_PROGRESS_RECORDS) {
+ // Size is acceptable, do nothing.
+ return;
+ }
+
+ // Make sure the temporary list is empty.
+ mTemporaryInProgressIndexes.clear();
+
+ // Populate the list with indexes for size of {@link mInProgressRecords}.
+ for (int i = 0; i < mInProgressRecords.size(); i++) {
+ mTemporaryInProgressIndexes.add(i, i);
+ }
+
+ // Sort the index collection by value of the corresponding key in {@link mInProgressRecords}
+ // from smallest to largest.
+ Collections.sort(mTemporaryInProgressIndexes, (a, b) -> Long.compare(
+ mInProgressRecords.keyAt(a), mInProgressRecords.keyAt(b)));
+
+ if (mTemporaryInProgressIndexes.size() == MAX_IN_PROGRESS_RECORDS + 1) {
+ // Only removing a single record so don't bother sorting again as we don't have to worry
+ // about indexes changing.
+ mInProgressRecords.removeAt(mTemporaryInProgressIndexes.get(0));
+ } else {
+ // Removing more than 1 record, remove the records we want to keep from the list and
+ // then sort again so we can remove in reverse order of indexes.
+ mTemporaryInProgressIndexes.subList(
+ mTemporaryInProgressIndexes.size() - MAX_IN_PROGRESS_RECORDS,
+ mTemporaryInProgressIndexes.size()).clear();
+ Collections.sort(mTemporaryInProgressIndexes);
+
+ // Remove all remaining record indexes in reverse order to avoid changing the already
+ // calculated indexes.
+ for (int i = mTemporaryInProgressIndexes.size() - 1; i >= 0; i--) {
+ mInProgressRecords.removeAt(mTemporaryInProgressIndexes.get(i));
+ }
+ }
+
+ // Clear the temorary list.
+ mTemporaryInProgressIndexes.clear();
+ }
+
void onIntentStarted(@NonNull Intent intent, long timestampNanos) {
synchronized (mLock) {
if (!mEnabled) {
@@ -211,7 +277,8 @@
} else {
start.setReason(ApplicationStartInfo.START_REASON_START_ACTIVITY);
}
- mInProgRecords.put(timestampNanos, start);
+ mInProgressRecords.put(timestampNanos, start);
+ maybeTrimInProgressRecordsLocked();
}
}
@@ -220,17 +287,17 @@
if (!mEnabled) {
return;
}
- int index = mInProgRecords.indexOfKey(id);
+ int index = mInProgressRecords.indexOfKey(id);
if (index < 0) {
return;
}
- ApplicationStartInfo info = mInProgRecords.valueAt(index);
+ ApplicationStartInfo info = mInProgressRecords.valueAt(index);
if (info == null) {
- mInProgRecords.removeAt(index);
+ mInProgressRecords.removeAt(index);
return;
}
info.setStartupState(ApplicationStartInfo.STARTUP_STATE_ERROR);
- mInProgRecords.removeAt(index);
+ mInProgressRecords.removeAt(index);
}
}
@@ -239,13 +306,13 @@
if (!mEnabled) {
return;
}
- int index = mInProgRecords.indexOfKey(id);
+ int index = mInProgressRecords.indexOfKey(id);
if (index < 0) {
return;
}
- ApplicationStartInfo info = mInProgRecords.valueAt(index);
+ ApplicationStartInfo info = mInProgressRecords.valueAt(index);
if (info == null || app == null) {
- mInProgRecords.removeAt(index);
+ mInProgressRecords.removeAt(index);
return;
}
info.setStartType((int) temperature);
@@ -254,9 +321,9 @@
if (newInfo == null) {
// newInfo can be null if records are added before load from storage is
// complete. In this case the newly added record will be lost.
- mInProgRecords.removeAt(index);
+ mInProgressRecords.removeAt(index);
} else {
- mInProgRecords.setValueAt(index, newInfo);
+ mInProgressRecords.setValueAt(index, newInfo);
}
}
}
@@ -266,17 +333,17 @@
if (!mEnabled) {
return;
}
- int index = mInProgRecords.indexOfKey(id);
+ int index = mInProgressRecords.indexOfKey(id);
if (index < 0) {
return;
}
- ApplicationStartInfo info = mInProgRecords.valueAt(index);
+ ApplicationStartInfo info = mInProgressRecords.valueAt(index);
if (info == null) {
- mInProgRecords.removeAt(index);
+ mInProgressRecords.removeAt(index);
return;
}
info.setStartupState(ApplicationStartInfo.STARTUP_STATE_ERROR);
- mInProgRecords.removeAt(index);
+ mInProgressRecords.removeAt(index);
}
}
@@ -286,13 +353,13 @@
if (!mEnabled) {
return;
}
- int index = mInProgRecords.indexOfKey(id);
+ int index = mInProgressRecords.indexOfKey(id);
if (index < 0) {
return;
}
- ApplicationStartInfo info = mInProgRecords.valueAt(index);
+ ApplicationStartInfo info = mInProgressRecords.valueAt(index);
if (info == null) {
- mInProgRecords.removeAt(index);
+ mInProgressRecords.removeAt(index);
return;
}
info.setLaunchMode(launchMode);
@@ -308,18 +375,18 @@
if (!mEnabled) {
return;
}
- int index = mInProgRecords.indexOfKey(id);
+ int index = mInProgressRecords.indexOfKey(id);
if (index < 0) {
return;
}
- ApplicationStartInfo info = mInProgRecords.valueAt(index);
+ ApplicationStartInfo info = mInProgressRecords.valueAt(index);
if (info == null) {
- mInProgRecords.removeAt(index);
+ mInProgressRecords.removeAt(index);
return;
}
info.addStartupTimestamp(ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN,
timestampNanos);
- mInProgRecords.removeAt(index);
+ mInProgressRecords.removeAt(index);
}
}
@@ -964,7 +1031,7 @@
mProcStartInfoFile.delete();
}
mData.getMap().clear();
- mInProgRecords.clear();
+ mInProgressRecords.clear();
}
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 29e0f7a..1ac37ad 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -128,8 +128,10 @@
import com.android.server.power.stats.BatteryStatsImpl;
import com.android.server.power.stats.BatteryUsageStatsProvider;
import com.android.server.power.stats.BluetoothPowerStatsProcessor;
+import com.android.server.power.stats.CameraPowerStatsProcessor;
import com.android.server.power.stats.CpuPowerStatsProcessor;
import com.android.server.power.stats.FlashlightPowerStatsProcessor;
+import com.android.server.power.stats.GnssPowerStatsProcessor;
import com.android.server.power.stats.MobileRadioPowerStatsProcessor;
import com.android.server.power.stats.PhoneCallPowerStatsProcessor;
import com.android.server.power.stats.PowerStatsAggregator;
@@ -528,8 +530,7 @@
AggregatedPowerStatsConfig.STATE_SCREEN,
AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
.setProcessor(
- new AudioPowerStatsProcessor(mPowerProfile,
- mPowerStatsUidResolver));
+ new AudioPowerStatsProcessor(mPowerProfile, mPowerStatsUidResolver));
config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_VIDEO)
.trackDeviceStates(
@@ -539,9 +540,7 @@
AggregatedPowerStatsConfig.STATE_POWER,
AggregatedPowerStatsConfig.STATE_SCREEN,
AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
- .setProcessor(
- new VideoPowerStatsProcessor(mPowerProfile,
- mPowerStatsUidResolver));
+ .setProcessor(new VideoPowerStatsProcessor(mPowerProfile, mPowerStatsUidResolver));
config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)
.trackDeviceStates(
@@ -552,8 +551,29 @@
AggregatedPowerStatsConfig.STATE_SCREEN,
AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
.setProcessor(
- new FlashlightPowerStatsProcessor(mPowerProfile,
- mPowerStatsUidResolver));
+ new FlashlightPowerStatsProcessor(mPowerProfile, mPowerStatsUidResolver));
+
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_CAMERA)
+ .trackDeviceStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
+ .trackUidStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+ .setProcessor(
+ new CameraPowerStatsProcessor(mPowerProfile, mPowerStatsUidResolver));
+
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_GNSS)
+ .trackDeviceStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
+ .trackUidStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+ .setProcessor(
+ new GnssPowerStatsProcessor(mPowerProfile, mPowerStatsUidResolver));
return config;
}
@@ -639,6 +659,12 @@
BatteryConsumer.POWER_COMPONENT_FLASHLIGHT,
Flags.streamlinedMiscBatteryStats());
+ mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_CAMERA,
+ Flags.streamlinedMiscBatteryStats());
+ mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+ BatteryConsumer.POWER_COMPONENT_CAMERA,
+ Flags.streamlinedMiscBatteryStats());
+
mWorker.systemServicesReady();
mStats.systemServicesReady(mContext);
mCpuWakeupStats.systemServicesReady();
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index ab34dd4..3b73e06 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -49,6 +49,7 @@
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_COMPONENT_DISABLED;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_EXECUTING_SERVICE;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_FINISH_RECEIVER;
+import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_FOLLOW_UP;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_GET_PROVIDER;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_NONE;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_PROCESS_BEGIN;
@@ -68,9 +69,7 @@
import static android.content.Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION;
-import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE;
-import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL;
import static android.media.audio.Flags.roForegroundAudioControl;
import static android.os.Process.THREAD_GROUP_BACKGROUND;
import static android.os.Process.THREAD_GROUP_DEFAULT;
@@ -91,6 +90,7 @@
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USAGE_STATS;
import static com.android.server.am.ActivityManagerService.DISPATCH_OOM_ADJ_OBSERVER_MSG;
+import static com.android.server.am.ActivityManagerService.FOLLOW_UP_OOMADJUSTER_UPDATE_MSG;
import static com.android.server.am.ActivityManagerService.IDLE_UIDS_MSG;
import static com.android.server.am.ActivityManagerService.TAG_BACKUP;
import static com.android.server.am.ActivityManagerService.TAG_LRU;
@@ -226,6 +226,8 @@
return AppProtoEnums.OOM_ADJ_REASON_RESTRICTION_CHANGE;
case OOM_ADJ_REASON_COMPONENT_DISABLED:
return AppProtoEnums.OOM_ADJ_REASON_COMPONENT_DISABLED;
+ case OOM_ADJ_REASON_FOLLOW_UP:
+ return AppProtoEnums.OOM_ADJ_REASON_FOLLOW_UP;
default:
return AppProtoEnums.OOM_ADJ_REASON_UNKNOWN_TO_PROTO;
}
@@ -280,6 +282,8 @@
return OOM_ADJ_REASON_METHOD + "_restrictionChange";
case OOM_ADJ_REASON_COMPONENT_DISABLED:
return OOM_ADJ_REASON_METHOD + "_componentDisabled";
+ case OOM_ADJ_REASON_FOLLOW_UP:
+ return OOM_ADJ_REASON_METHOD + "_followUp";
default:
return "_unknown";
}
@@ -370,6 +374,7 @@
protected final int[] mTmpSchedGroup = new int[1];
final ActivityManagerService mService;
+ final Injector mInjector;
final ProcessList mProcessList;
final ActivityManagerGlobalLock mProcLock;
@@ -384,6 +389,13 @@
protected final ArraySet<ProcessRecord> mProcessesInCycle = new ArraySet<>();
/**
+ * List of processes that we want to batch for LMKD to adjust their respective
+ * OOM scores.
+ */
+ @GuardedBy("mService")
+ protected final ArrayList<ProcessRecord> mProcsToOomAdj = new ArrayList<ProcessRecord>();
+
+ /**
* Flag to mark if there is an ongoing oomAdjUpdate: potentially the oomAdjUpdate
* could be called recursively because of the indirect calls during the update;
* however the oomAdjUpdate itself doesn't support recursion - in this case we'd
@@ -413,12 +425,33 @@
@GuardedBy("mService")
protected int mProcessStateCurTop = PROCESS_STATE_TOP;
- /** Overrideable by a test */
+ @GuardedBy("mService")
+ private final ArraySet<ProcessRecord> mFollowUpUpdateSet = new ArraySet<>();
+
+ private static final long NO_FOLLOW_UP_TIME = Long.MAX_VALUE;
+ @GuardedBy("mService")
+ private long mNextFollowUpUpdateUptimeMs = NO_FOLLOW_UP_TIME;
+
@VisibleForTesting
- protected boolean isChangeEnabled(@CachedCompatChangeId int cachedCompatChangeId,
+ public static class Injector {
+ boolean isChangeEnabled(@CachedCompatChangeId int cachedCompatChangeId,
+ ApplicationInfo app, boolean defaultValue) {
+ return PlatformCompatCache.getInstance()
+ .isChangeEnabled(cachedCompatChangeId, app, defaultValue);
+ }
+
+ long getUptimeMillis() {
+ return SystemClock.uptimeMillis();
+ }
+
+ long getElapsedRealtimeMillis() {
+ return SystemClock.elapsedRealtime();
+ }
+ }
+
+ boolean isChangeEnabled(@CachedCompatChangeId int cachedCompatChangeId,
ApplicationInfo app, boolean defaultValue) {
- return PlatformCompatCache.getInstance()
- .isChangeEnabled(cachedCompatChangeId, app, defaultValue);
+ return mInjector.isChangeEnabled(cachedCompatChangeId, app, defaultValue);
}
OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids) {
@@ -436,7 +469,18 @@
OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids,
ServiceThread adjusterThread) {
+ this(service, processList, activeUids, adjusterThread, new Injector());
+ }
+
+ OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids,
+ Injector injector) {
+ this(service, processList, activeUids, createAdjusterThread(), injector);
+ }
+
+ OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids,
+ ServiceThread adjusterThread, Injector injector) {
mService = service;
+ mInjector = injector;
mProcessList = processList;
mProcLock = service.mProcLock;
mActiveUids = activeUids;
@@ -624,8 +668,8 @@
// processes, its adj could be still unknown as of now, assign one.
processes.add(app);
assignCachedAdjIfNecessary(processes);
- applyOomAdjLSP(app, false, SystemClock.uptimeMillis(),
- SystemClock.elapsedRealtime(), oomAdjReason);
+ applyOomAdjLSP(app, false, mInjector.getUptimeMillis(),
+ mInjector.getElapsedRealtimeMillis(), oomAdjReason);
}
mTmpProcessList.clear();
mService.clearPendingTopAppLocked();
@@ -842,6 +886,40 @@
}
@GuardedBy("mService")
+ void updateOomAdjFollowUpTargetsLocked() {
+ final long now = mInjector.getUptimeMillis();
+ long nextFollowUpUptimeMs = Long.MAX_VALUE;
+ mNextFollowUpUpdateUptimeMs = NO_FOLLOW_UP_TIME;
+ for (int i = mFollowUpUpdateSet.size() - 1; i >= 0; i--) {
+ final ProcessRecord proc = mFollowUpUpdateSet.valueAtUnchecked(i);
+ final long followUpUptimeMs = proc.mState.getFollowupUpdateUptimeMs();
+
+ if (proc.isKilled()) {
+ // Process is dead, just remove from follow up set.
+ mFollowUpUpdateSet.removeAt(i);
+ } else if (followUpUptimeMs <= now) {
+ // Add processes that need a follow up update.
+ mPendingProcessSet.add(proc);
+ proc.mState.setFollowupUpdateUptimeMs(NO_FOLLOW_UP_TIME);
+ mFollowUpUpdateSet.removeAt(i);
+ } else if (followUpUptimeMs < nextFollowUpUptimeMs) {
+ // Figure out when to schedule the next follow up update.
+ nextFollowUpUptimeMs = followUpUptimeMs;
+ } else if (followUpUptimeMs == NO_FOLLOW_UP_TIME) {
+ // The follow up is no longer needed for this process.
+ mFollowUpUpdateSet.removeAt(i);
+ }
+ }
+
+ if (nextFollowUpUptimeMs != Long.MAX_VALUE) {
+ // There is still at least one process that needs a follow up.
+ scheduleFollowUpOomAdjusterUpdateLocked(nextFollowUpUptimeMs, now);
+ }
+
+ updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_FOLLOW_UP);
+ }
+
+ @GuardedBy("mService")
protected void performUpdateOomAdjPendingTargetsLocked(@OomAdjReason int oomAdjReason) {
final ProcessRecord topApp = mService.getTopApp();
@@ -892,8 +970,8 @@
if (startProfiling) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
}
- final long now = SystemClock.uptimeMillis();
- final long nowElapsed = SystemClock.elapsedRealtime();
+ final long now = mInjector.getUptimeMillis();
+ final long nowElapsed = mInjector.getElapsedRealtimeMillis();
final long oldTime = now - mConstants.mMaxEmptyTimeMillis;
final int numProc = activeProcesses.size();
@@ -1019,7 +1097,7 @@
updateUidsLSP(activeUids, nowElapsed);
synchronized (mService.mProcessStats.mLock) {
- final long nowUptime = SystemClock.uptimeMillis();
+ final long nowUptime = mInjector.getUptimeMillis();
if (mService.mProcessStats.shouldWriteNowLocked(nowUptime)) {
mService.mHandler.post(new ActivityManagerService.ProcStatsRunnable(mService,
mService.mProcessStats));
@@ -1030,7 +1108,7 @@
}
if (DEBUG_OOM_ADJ) {
- final long duration = SystemClock.uptimeMillis() - now;
+ final long duration = mInjector.getUptimeMillis() - now;
if (false) {
Slog.d(TAG_OOM_ADJ, "Did OOM ADJ in " + duration + "ms",
new RuntimeException("here").fillInStackTrace());
@@ -1044,7 +1122,7 @@
protected void assignCachedAdjIfNecessary(ArrayList<ProcessRecord> lruList) {
final int numLru = lruList.size();
if (mConstants.USE_TIERED_CACHED_ADJ) {
- final long now = SystemClock.uptimeMillis();
+ final long now = mInjector.getUptimeMillis();
for (int i = numLru - 1; i >= 0; i--) {
ProcessRecord app = lruList.get(i);
final ProcessStateRecord state = app.mState;
@@ -1246,7 +1324,7 @@
if (!app.isKilledByAm() && app.getThread() != null) {
// We don't need to apply the update for the process which didn't get computed
if (state.getCompletedAdjSeq() == mAdjSeq) {
- applyOomAdjLSP(app, doingAll, now, nowElapsed, oomAdjReason);
+ applyOomAdjLSP(app, doingAll, now, nowElapsed, oomAdjReason, true);
}
if (app.isPendingFinishAttach()) {
@@ -1348,6 +1426,11 @@
}
}
+ if (!mProcsToOomAdj.isEmpty()) {
+ ProcessList.batchSetOomAdj(mProcsToOomAdj);
+ mProcsToOomAdj.clear();
+ }
+
if (proactiveKillsEnabled // Proactive kills enabled?
&& doKillExcessiveProcesses // Should kill excessive processes?
&& freeSwapPercent < lowSwapThresholdPercent // Swap below threshold?
@@ -1722,6 +1805,10 @@
int prevProcState = getInitialProcState(app);
int prevCapability = getInitialCapability(app);
+ // Remove any follow up update this process might have. It will be rescheduled if still
+ // needed.
+ app.mState.setFollowupUpdateUptimeMs(NO_FOLLOW_UP_TIME);
+
if (app.getThread() == null) {
state.setAdjSeq(mAdjSeq);
state.setCurrentSchedulingGroup(SCHED_GROUP_BACKGROUND);
@@ -1989,6 +2076,8 @@
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to recent fg: " + app);
}
+ maybeSetProcessFollowUpUpdateLocked(app,
+ state.getLastTopTime() + mConstants.TOP_TO_FGS_GRACE_DURATION, now);
}
// If the app was recently in the foreground and has expedited jobs running,
@@ -2009,6 +2098,9 @@
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to recent fg for EJ: " + app);
}
+ maybeSetProcessFollowUpUpdateLocked(app,
+ state.getLastTopTime() + mConstants.TOP_TO_ALMOST_PERCEPTIBLE_GRACE_DURATION,
+ now);
}
if (adj > PERCEPTIBLE_APP_ADJ
@@ -2074,7 +2166,7 @@
// app to be demoted to cached.
if (procState >= PROCESS_STATE_LAST_ACTIVITY
&& state.getSetProcState() == PROCESS_STATE_LAST_ACTIVITY
- && (state.getLastStateTime() + mConstants.MAX_PREVIOUS_TIME) < now) {
+ && (state.getLastStateTime() + mConstants.MAX_PREVIOUS_TIME) <= now) {
procState = PROCESS_STATE_LAST_ACTIVITY;
schedGroup = SCHED_GROUP_BACKGROUND;
state.setAdjType("previous-expired");
@@ -2098,6 +2190,14 @@
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to prev: " + app);
}
}
+ final long lastStateTime;
+ if (state.getSetProcState() == PROCESS_STATE_LAST_ACTIVITY) {
+ lastStateTime = state.getLastStateTime();
+ } else {
+ lastStateTime = now;
+ }
+ maybeSetProcessFollowUpUpdateLocked(app,
+ lastStateTime + mConstants.MAX_PREVIOUS_TIME, now);
}
}
@@ -2186,6 +2286,8 @@
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise adj to started service: " + app);
}
+ maybeSetProcessFollowUpUpdateLocked(app,
+ s.lastActivity + mConstants.MAX_SERVICE_INACTIVITY, now);
}
}
// If we have let the service slide into the background
@@ -2205,14 +2307,8 @@
!= 0 ? PROCESS_CAPABILITY_FOREGROUND_LOCATION : 0;
if (roForegroundAudioControl()) { // flag check
- // TODO revisit restriction of FOREGROUND_AUDIO_CONTROL when it can be
- // limited to specific FGS types
- //final int fgsAudioType = FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
- // | FOREGROUND_SERVICE_TYPE_CAMERA
- // | FOREGROUND_SERVICE_TYPE_MICROPHONE
- // | FOREGROUND_SERVICE_TYPE_PHONE_CALL;
- //capabilityFromFGS |= (psr.getForegroundServiceTypes() & fgsAudioType) != 0
- // ? PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL : 0;
+ // TODO(b/335373208) - revisit restriction of FOREGROUND_AUDIO_CONTROL
+ // when it can be limited to specific FGS types
capabilityFromFGS |= PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL;
}
@@ -2340,6 +2436,8 @@
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise adj to recent provider: " + app);
}
+ maybeSetProcessFollowUpUpdateLocked(app,
+ ppr.getLastProviderTime() + mConstants.CONTENT_PROVIDER_RETAIN_TIME, now);
}
if (procState > PROCESS_STATE_LAST_ACTIVITY) {
procState = PROCESS_STATE_LAST_ACTIVITY;
@@ -2348,6 +2446,8 @@
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise procstate to recent provider: " + app);
}
+ maybeSetProcessFollowUpUpdateLocked(app,
+ ppr.getLastProviderTime() + mConstants.CONTENT_PROVIDER_RETAIN_TIME, now);
}
}
@@ -3246,10 +3346,16 @@
mCachedAppOptimizer.onWakefulnessChanged(wakefulness);
}
+ @GuardedBy({"mService", "mProcLock"})
+ protected boolean applyOomAdjLSP(ProcessRecord app, boolean doingAll, long now,
+ long nowElapsed, @OomAdjReason int oomAdjReason) {
+ return applyOomAdjLSP(app, doingAll, now, nowElapsed, oomAdjReason, false);
+ }
+
/** Applies the computed oomadj, procstate and sched group values and freezes them in set* */
@GuardedBy({"mService", "mProcLock"})
protected boolean applyOomAdjLSP(ProcessRecord app, boolean doingAll, long now,
- long nowElapsed, @OomAdjReason int oomAdjReson) {
+ long nowElapsed, @OomAdjReason int oomAdjReson, boolean isBatchingOomAdj) {
boolean success = true;
final ProcessStateRecord state = app.mState;
final UidRecord uidRec = app.getUidRecord();
@@ -3266,7 +3372,12 @@
final int oldOomAdj = state.getSetAdj();
if (state.getCurAdj() != state.getSetAdj()) {
- ProcessList.setOomAdj(app.getPid(), app.uid, state.getCurAdj());
+ if (isBatchingOomAdj && mConstants.ENABLE_BATCHING_OOM_ADJ) {
+ mProcsToOomAdj.add(app);
+ } else {
+ ProcessList.setOomAdj(app.getPid(), app.uid, state.getCurAdj());
+ }
+
if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.info.uid) {
String msg = "Set " + app.getPid() + " " + app.processName + " adj "
+ state.getCurAdj() + ": " + state.getAdjType();
@@ -3649,7 +3760,7 @@
if (N <= 0) {
return;
}
- final long nowElapsed = SystemClock.elapsedRealtime();
+ final long nowElapsed = mInjector.getElapsedRealtimeMillis();
final long maxBgTime = nowElapsed - mConstants.BACKGROUND_SETTLE_TIME;
long nextTime = 0;
if (mService.mLocalPowerManager != null) {
@@ -3884,7 +3995,7 @@
}
// Take a dry run of the computeServiceHostOomAdjLSP, this would't be expensive
// since it's only evaluating one service connection.
- return computeServiceHostOomAdjLSP(cr, app, client, SystemClock.uptimeMillis(),
+ return computeServiceHostOomAdjLSP(cr, app, client, mInjector.getUptimeMillis(),
mService.getTopApp(), false, false, false, OOM_ADJ_REASON_NONE,
CACHED_APP_MIN_ADJ, false, true /* dryRun */);
}
@@ -3919,7 +4030,7 @@
// The provider host process has better states than the client, so no change.
return false;
}
- return computeProviderHostOomAdjLSP(null, app, client, SystemClock.uptimeMillis(),
+ return computeProviderHostOomAdjLSP(null, app, client, mInjector.getUptimeMillis(),
mService.getTopApp(), false, false, false, OOM_ADJ_REASON_NONE, CACHED_APP_MIN_ADJ,
false, true /* dryRun */);
}
@@ -3947,4 +4058,43 @@
}
return false;
}
+
+ @GuardedBy("mService")
+ private void maybeSetProcessFollowUpUpdateLocked(ProcessRecord proc,
+ long updateUptimeMs, long now) {
+ if (!Flags.followUpOomadjUpdates()) {
+ return;
+ }
+ if (updateUptimeMs <= now) {
+ // Time sensitive period has already passed. No need to schedule a follow up.
+ return;
+ }
+
+ mFollowUpUpdateSet.add(proc);
+ proc.mState.setFollowupUpdateUptimeMs(updateUptimeMs);
+
+ scheduleFollowUpOomAdjusterUpdateLocked(updateUptimeMs, now);
+ }
+
+
+ @GuardedBy("mService")
+ private void scheduleFollowUpOomAdjusterUpdateLocked(long updateUptimeMs,
+ long now) {
+ if (updateUptimeMs + mConstants.FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION
+ >= mNextFollowUpUpdateUptimeMs) {
+ // Update time is too close or later than the next follow up update.
+ return;
+ }
+ if (updateUptimeMs < now + mConstants.FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION) {
+ // Use a minimum delay for the follow up to possibly batch multiple process
+ // evaluations and avoid rapid updates.
+ updateUptimeMs = now + mConstants.FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION;
+ }
+
+ // Schedulate a follow up update. Don't bother deleting existing handler messages, they
+ // will be cleared during the message while no locks are being held.
+ mNextFollowUpUpdateUptimeMs = updateUptimeMs;
+ mService.mHandler.sendEmptyMessageAtTime(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG,
+ mNextFollowUpUpdateUptimeMs);
+ }
}
diff --git a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
index a67af89..21842db 100644
--- a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
+++ b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
@@ -68,7 +68,6 @@
import android.app.ActivityManagerInternal.OomAdjReason;
import android.content.pm.ServiceInfo;
import android.os.IBinder;
-import android.os.SystemClock;
import android.os.Trace;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -764,6 +763,11 @@
super(service, processList, activeUids, adjusterThread);
}
+ OomAdjusterModernImpl(ActivityManagerService service, ProcessList processList,
+ ActiveUids activeUids, Injector injector) {
+ super(service, processList, activeUids, injector);
+ }
+
private final ProcessRecordNodes mProcessRecordProcStateNodes = new ProcessRecordNodes(
ProcessRecordNode.NODE_TYPE_PROC_STATE, PROC_STATE_SLOTS.length);
private final ProcessRecordNodes mProcessRecordAdjNodes = new ProcessRecordNodes(
@@ -924,8 +928,8 @@
@GuardedBy({"mService", "mProcLock"})
private void fullUpdateLSP(@OomAdjReason int oomAdjReason) {
final ProcessRecord topApp = mService.getTopApp();
- final long now = SystemClock.uptimeMillis();
- final long nowElapsed = SystemClock.elapsedRealtime();
+ final long now = mInjector.getUptimeMillis();
+ final long nowElapsed = mInjector.getElapsedRealtimeMillis();
final long oldTime = now - mConstants.mMaxEmptyTimeMillis;
mAdjSeq++;
@@ -1015,8 +1019,8 @@
@GuardedBy({"mService", "mProcLock"})
private void partialUpdateLSP(@OomAdjReason int oomAdjReason, ArraySet<ProcessRecord> targets) {
final ProcessRecord topApp = mService.getTopApp();
- final long now = SystemClock.uptimeMillis();
- final long nowElapsed = SystemClock.elapsedRealtime();
+ final long now = mInjector.getUptimeMillis();
+ final long nowElapsed = mInjector.getElapsedRealtimeMillis();
final long oldTime = now - mConstants.mMaxEmptyTimeMillis;
ActiveUids activeUids = mTmpUidRecords;
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 219de70..c094724 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -354,6 +354,7 @@
// LMK_KILL_OCCURRED
// LMK_START_MONITORING
// LMK_BOOT_COMPLETED
+ // LMK_PROCS_PRIO
static final byte LMK_TARGET = 0;
static final byte LMK_PROCPRIO = 1;
static final byte LMK_PROCREMOVE = 2;
@@ -365,6 +366,7 @@
static final byte LMK_KILL_OCCURRED = 8; // Msg to subscribed clients on kill occurred event
static final byte LMK_START_MONITORING = 9; // Start monitoring if delayed earlier
static final byte LMK_BOOT_COMPLETED = 10;
+ static final byte LMK_PROCS_PRIO = 11; // Batch option for LMK_PROCPRIO
// Low Memory Killer Daemon command codes.
// These must be kept in sync with async_event_type definitions in lmkd.h
@@ -1561,6 +1563,50 @@
}
}
+
+ // The max size for PROCS_PRIO cmd in LMKD
+ private static final int MAX_PROCS_PRIO_PACKET_SIZE = 3;
+
+ // (4 bytes per field * 4 fields * 3 processes per batch) + 4 bytes for the LMKD cmd
+ private static final int MAX_OOM_ADJ_BATCH_LENGTH = ((4 * 4) * MAX_PROCS_PRIO_PACKET_SIZE) + 4;
+
+ /**
+ * Set the out-of-memory badness adjustment for a list of processes.
+ *
+ * @param apps App list to adjust their respective oom score.
+ *
+ * {@hide}
+ */
+ public static void batchSetOomAdj(ArrayList<ProcessRecord> apps) {
+ final int totalApps = apps.size();
+ if (totalApps == 0) {
+ return;
+ }
+
+ ByteBuffer buf = ByteBuffer.allocate(MAX_OOM_ADJ_BATCH_LENGTH);
+ int total_procs_in_buf = 0;
+ buf.putInt(LMK_PROCS_PRIO);
+ for (int i = 0; i < totalApps; i++) {
+ final int pid = apps.get(i).getPid();
+ final int amt = apps.get(i).mState.getCurAdj();
+ final int uid = apps.get(i).uid;
+ if (pid <= 0 || amt == UNKNOWN_ADJ) continue;
+ if (total_procs_in_buf >= MAX_PROCS_PRIO_PACKET_SIZE) {
+ writeLmkd(buf, null);
+ buf.clear();
+ total_procs_in_buf = 0;
+ buf.allocate(MAX_OOM_ADJ_BATCH_LENGTH);
+ buf.putInt(LMK_PROCS_PRIO);
+ }
+ buf.putInt(pid);
+ buf.putInt(uid);
+ buf.putInt(amt);
+ buf.putInt(0); // Default proc type to PROC_TYPE_APP
+ total_procs_in_buf++;
+ }
+ writeLmkd(buf, null);
+ }
+
/*
* {@hide}
*/
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
index 8de748e..7c64298 100644
--- a/services/core/java/com/android/server/am/ProcessStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -449,6 +449,9 @@
@GuardedBy("mService")
private boolean mScheduleLikeTopApp = false;
+ @GuardedBy("mService")
+ private long mFollowupUpdateUptimeMs = Long.MAX_VALUE;
+
ProcessStateRecord(ProcessRecord app) {
mApp = app;
mService = app.mService;
@@ -1164,6 +1167,16 @@
mScheduleLikeTopApp = scheduleLikeTopApp;
}
+ @GuardedBy("mService")
+ long getFollowupUpdateUptimeMs() {
+ return mFollowupUpdateUptimeMs;
+ }
+
+ @GuardedBy("mService")
+ void setFollowupUpdateUptimeMs(long updateUptimeMs) {
+ mFollowupUpdateUptimeMs = updateUptimeMs;
+ }
+
@GuardedBy(anyOf = {"mService", "mProcLock"})
public String makeAdjReason() {
if (mAdjSource != null || mAdjTarget != null) {
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 9a3b575..cbea7aa 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -142,6 +142,7 @@
"arc_next",
"art_mainline",
"art_performance",
+ "attack_tools",
"avic",
"biometrics",
"biometrics_framework",
@@ -172,6 +173,7 @@
"haptics",
"hardware_backed_security_mainline",
"input",
+ "llvm_and_toolchains",
"lse_desktop_experience",
"machine_learning",
"mainline_modularization",
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index afde4f7..bb52857 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -134,3 +134,21 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "batching_oom_adj"
+ namespace: "backstage_power"
+ description: "Batch OOM adjustment calls to LMKD"
+ bug: "244232958"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "follow_up_oomadj_updates"
+ namespace: "backstage_power"
+ description: "Schedule follow up OomAdjuster updates for time sensitive states."
+ bug: "333450932"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 039e7f4..bce1830 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -275,7 +275,7 @@
* Handle BluetoothHeadset intents where the action is one of
* {@link BluetoothHeadset#ACTION_ACTIVE_DEVICE_CHANGED} or
* {@link BluetoothHeadset#ACTION_AUDIO_STATE_CHANGED}.
- * @param intent
+ * @param intent the Intent received from BT service
*/
private void onReceiveBtEvent(@NonNull Intent intent) {
mBtHelper.onReceiveBtEvent(intent);
@@ -296,7 +296,7 @@
/**
* Turns speakerphone on/off
- * @param on
+ * @param on true to enable speakerphone
* @param eventSource for logging purposes
*/
/*package*/ void setSpeakerphoneOn(
@@ -310,21 +310,21 @@
on, BtHelper.SCO_MODE_UNDEFINED, eventSource, isPrivileged));
}
+ private static final long SET_COMMUNICATION_DEVICE_TIMEOUT_MS = 3000;
+
+ /** synchronization for setCommunicationDevice() and getCommunicationDevice */
+ private final Object mCommunicationDeviceLock = new Object();
+ @GuardedBy("mCommunicationDeviceLock")
+ private int mCommunicationDeviceUpdateCount = 0;
+
/**
* Select device for use for communication use cases.
* @param cb Client binder for death detection
* @param uid Client uid
* @param device Device selected or null to unselect.
* @param eventSource for logging purposes
+ * @return false if there is no device and no communication client
*/
-
- private static final long SET_COMMUNICATION_DEVICE_TIMEOUT_MS = 3000;
-
- /** synchronization for setCommunicationDevice() and getCommunicationDevice */
- private Object mCommunicationDeviceLock = new Object();
- @GuardedBy("mCommunicationDeviceLock")
- private int mCommunicationDeviceUpdateCount = 0;
-
/*package*/ boolean setCommunicationDevice(IBinder cb, int uid, AudioDeviceInfo device,
boolean isPrivileged, String eventSource) {
@@ -355,7 +355,6 @@
* Sets or resets the communication device for matching client. If no client matches and the
* request is to reset for a given device (deviceInfo.mOn == false), the method is a noop.
* @param deviceInfo information on the device and requester {@link #CommunicationDeviceInfo}
- * @return true if the communication device is set or reset
*/
@GuardedBy("mDeviceStateLock")
/*package*/ void onSetCommunicationDeviceForClient(CommunicationDeviceInfo deviceInfo) {
@@ -742,15 +741,6 @@
}
/**
- * Helper method on top of isDeviceRequestedForCommunication() indicating if
- * speakerphone ON is currently requested or not.
- * @return true if speakerphone ON requested, false otherwise.
- */
- private boolean isSpeakerphoneRequested() {
- return isDeviceRequestedForCommunication(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
- }
-
- /**
* Indicates if preferred route selection for communication is speakerphone.
* @return true if speakerphone is active, false otherwise.
*/
@@ -929,6 +919,12 @@
}
@Override
+ public int hashCode() {
+ // only hashing on the fields used in equals()
+ return Objects.hash(mProfile, mDevice);
+ }
+
+ @Override
public String toString() {
return "BtDeviceInfo: device=" + mDevice.toString()
+ " state=" + mState
@@ -990,7 +986,7 @@
/**
* will block on mDeviceStateLock, which is held during an A2DP (dis) connection
* not just a simple message post
- * @param info struct with the (dis)connection information
+ * @param data struct with the (dis)connection information
*/
/*package*/ void queueOnBluetoothActiveDeviceChanged(@NonNull BtDeviceChangedData data) {
if (data.mPreviousDevice != null
@@ -1379,6 +1375,7 @@
mCommDevDispatchers.getBroadcastItem(i)
.dispatchCommunicationDeviceChanged(portId);
} catch (RemoteException e) {
+ Log.e(TAG, "dispatchCommunicationDevice error", e);
}
}
mCommDevDispatchers.finishBroadcast();
@@ -1581,6 +1578,12 @@
}
@Override
+ public int hashCode() {
+ // only hashing on the fields used in equals()
+ return Objects.hash(mCb.hashCode(), mUid);
+ }
+
+ @Override
public String toString() {
return "CommunicationDeviceInfo mCb=" + mCb.toString()
+ " mUid=" + mUid
@@ -1600,9 +1603,9 @@
//@GuardedBy("mConnectedDevices")
/*package*/ void setBluetoothA2dpOnInt(boolean on, boolean fromA2dp, String source) {
// for logging only
- final String eventSource = new StringBuilder("setBluetoothA2dpOn(").append(on)
- .append(") from u/pid:").append(Binder.getCallingUid()).append("/")
- .append(Binder.getCallingPid()).append(" src:").append(source).toString();
+ final String eventSource = "setBluetoothA2dpOn(" + on
+ + ") from u/pid:" + Binder.getCallingUid() + "/"
+ + Binder.getCallingPid() + " src:" + source;
mBluetoothA2dpEnabled.set(on);
onSetForceUse(
@@ -2203,10 +2206,6 @@
sendIILMsg(msg, existingMsgPolicy, 0, 0, obj, delay);
}
- private void sendIMsg(int msg, int existingMsgPolicy, int arg, int delay) {
- sendIILMsg(msg, existingMsgPolicy, arg, 0, null, delay);
- }
-
private void sendMsgNoDelay(int msg, int existingMsgPolicy) {
sendIILMsg(msg, existingMsgPolicy, 0, 0, null, 0);
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 287c92f..ba7aee0 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -449,7 +449,7 @@
@Override
public DeviceInfo put(String key, DeviceInfo value) {
final DeviceInfo result = super.put(key, value);
- record("put", true /* connected */, key, value);
+ record("put", true /* connected */, value);
return result;
}
@@ -457,7 +457,7 @@
public DeviceInfo putIfAbsent(String key, DeviceInfo value) {
final DeviceInfo result = super.putIfAbsent(key, value);
if (result == null) {
- record("putIfAbsent", true /* connected */, key, value);
+ record("putIfAbsent", true /* connected */, value);
}
return result;
}
@@ -466,7 +466,7 @@
public DeviceInfo remove(Object key) {
final DeviceInfo result = super.remove(key);
if (result != null) {
- record("remove", false /* connected */, (String) key, result);
+ record("remove", false /* connected */, result);
}
return result;
}
@@ -475,7 +475,7 @@
public boolean remove(Object key, Object value) {
final boolean result = super.remove(key, value);
if (result) {
- record("remove", false /* connected */, (String) key, (DeviceInfo) value);
+ record("remove", false /* connected */, (DeviceInfo) value);
}
return result;
}
@@ -489,7 +489,7 @@
// putAll
// replace
// replaceAll
- private void record(String event, boolean connected, String key, DeviceInfo value) {
+ private void record(String event, boolean connected, DeviceInfo value) {
// DeviceInfo - int mDeviceType;
// DeviceInfo - int mDeviceCodecFormat;
new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE
@@ -662,7 +662,7 @@
/**
* A class just for packaging up a set of connection parameters.
*/
- /*package*/ class WiredDeviceConnectionState {
+ /*package*/ static class WiredDeviceConnectionState {
public final AudioDeviceAttributes mAttributes;
public final @AudioService.ConnectionState int mState;
public final String mCaller;
@@ -1054,7 +1054,9 @@
IAudioRoutesObserver obs = mRoutesObservers.getBroadcastItem(n);
try {
obs.dispatchAudioRoutesChanged(routes);
- } catch (RemoteException e) { }
+ } catch (RemoteException e) {
+ Log.e(TAG, "onReportNewRoutes", e);
+ }
}
}
mRoutesObservers.finishBroadcast();
@@ -1835,7 +1837,8 @@
.set(MediaMetrics.Property.EVENT, "disconnectHearingAid")
.record();
if (toRemove.size() > 0) {
- final int delay = checkSendBecomingNoisyIntentInt(DEVICE_OUT_HEARING_AID,
+ /*final int delay = */
+ checkSendBecomingNoisyIntentInt(DEVICE_OUT_HEARING_AID,
AudioService.CONNECTION_STATE_DISCONNECTED, AudioSystem.DEVICE_NONE);
toRemove.stream().forEach(deviceAddress ->
// TODO delay not used?
@@ -2697,10 +2700,6 @@
private static final String CONNECT_INTENT_KEY_PORT_NAME = "portName";
private static final String CONNECT_INTENT_KEY_STATE = "state";
private static final String CONNECT_INTENT_KEY_ADDRESS = "address";
- private static final String CONNECT_INTENT_KEY_HAS_PLAYBACK = "hasPlayback";
- private static final String CONNECT_INTENT_KEY_HAS_CAPTURE = "hasCapture";
- private static final String CONNECT_INTENT_KEY_HAS_MIDI = "hasMIDI";
- private static final String CONNECT_INTENT_KEY_DEVICE_CLASS = "class";
private void sendDeviceConnectionIntent(int device, int state, String address,
String deviceName) {
@@ -2863,6 +2862,7 @@
mPrefDevDispatchers.getBroadcastItem(i).dispatchPrefDevicesChanged(
strategy, devices);
} catch (RemoteException e) {
+ Log.e(TAG, "dispatchPreferredDevice ", e);
}
}
mPrefDevDispatchers.finishBroadcast();
@@ -2879,6 +2879,7 @@
mNonDefDevDispatchers.getBroadcastItem(i).dispatchNonDefDevicesChanged(
strategy, devices);
} catch (RemoteException e) {
+ Log.e(TAG, "dispatchNonDefaultDevice ", e);
}
}
mNonDefDevDispatchers.finishBroadcast();
@@ -2895,6 +2896,7 @@
mDevRoleCapturePresetDispatchers.getBroadcastItem(i).dispatchDevicesRoleChanged(
capturePreset, role, devices);
} catch (RemoteException e) {
+ Log.e(TAG, "dispatchDevicesRoleForCapturePreset ", e);
}
}
mDevRoleCapturePresetDispatchers.finishBroadcast();
@@ -2960,7 +2962,7 @@
/**
* Check if device is in the list of connected devices
- * @param device
+ * @param device the device to query
* @return true if connected
*/
@VisibleForTesting
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index c7ddccc..5dd1480 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -10331,7 +10331,7 @@
try {
if (!permissionOverridesCheck && mHardeningEnforcer.blockFocusMethod(uid,
HardeningEnforcer.METHOD_AUDIO_MANAGER_REQUEST_AUDIO_FOCUS,
- clientId, durationHint, callingPackageName)) {
+ clientId, durationHint, callingPackageName, attributionTag, sdk)) {
final String reason = "Audio focus request blocked by hardening";
Log.w(TAG, reason);
mmi.set(MediaMetrics.Property.EARLY_RETURN, reason).record();
@@ -10343,7 +10343,7 @@
mmi.record();
return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,
- clientId, callingPackageName, attributionTag, flags, sdk,
+ clientId, callingPackageName, flags, sdk,
forceFocusDuckingForAccessibility(aa, durationHint, uid), -1 /*testUid, ignored*/,
permissionOverridesCheck);
}
@@ -10361,7 +10361,7 @@
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,
- clientId, callingPackageName, null, flags,
+ clientId, callingPackageName, flags,
sdk, false /*forceDuck*/, fakeUid, true /*permissionOverridesCheck*/);
}
diff --git a/services/core/java/com/android/server/audio/HardeningEnforcer.java b/services/core/java/com/android/server/audio/HardeningEnforcer.java
index 409ed17..8ae04ac 100644
--- a/services/core/java/com/android/server/audio/HardeningEnforcer.java
+++ b/services/core/java/com/android/server/audio/HardeningEnforcer.java
@@ -19,6 +19,7 @@
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.content.Context;
@@ -26,6 +27,7 @@
import android.media.AudioFocusRequest;
import android.media.AudioManager;
import android.os.Binder;
+import android.os.Build;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Slog;
@@ -128,19 +130,28 @@
* @param focusMethod name of the method to check, for logging purposes
* @param clientId id of the requester
* @param durationHint focus type being requested
+ * @param attributionTag attribution of the caller
+ * @param targetSdk target SDK of the caller
* @return false if the method call is allowed, true if it should be a no-op
*/
+ @SuppressWarnings("AndroidFrameworkCompatChange")
protected boolean blockFocusMethod(int callingUid, int focusMethod, @NonNull String clientId,
- int durationHint, @NonNull String packageName) {
+ int durationHint, @NonNull String packageName, String attributionTag, int targetSdk) {
if (packageName.isEmpty()) {
packageName = getPackNameForUid(callingUid);
}
- if (checkAppOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, callingUid, packageName)) {
+ if (noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, callingUid, packageName, attributionTag)) {
if (DEBUG) {
Slog.i(TAG, "blockFocusMethod pack:" + packageName + " NOT blocking");
}
return false;
+ } else if (targetSdk < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+ if (DEBUG) {
+ Slog.i(TAG, "blockFocusMethod pack:" + packageName + " NOT blocking due to sdk="
+ + targetSdk);
+ }
+ return false;
}
String errorMssg = "Focus request DENIED for uid:" + callingUid
@@ -169,14 +180,17 @@
}
/**
- * Checks the given op without throwing
+ * Notes the given op without throwing
* @param op the appOp code
* @param uid the calling uid
* @param packageName the package name of the caller
+ * @param attributionTag attribution of the caller
* @return return false if the operation is not allowed
*/
- private boolean checkAppOp(int op, int uid, @NonNull String packageName) {
- if (mAppOps.checkOpNoThrow(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) {
+ private boolean noteOp(int op, int uid, @NonNull String packageName,
+ @Nullable String attributionTag) {
+ if (mAppOps.noteOpNoThrow(op, uid, packageName, attributionTag, null)
+ != AppOpsManager.MODE_ALLOWED) {
return false;
}
return true;
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index 35d38e2..70f3193 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -1082,7 +1082,6 @@
* @param fd
* @param clientId
* @param callingPackageName
- * @param attributionTag
* @param flags
* @param sdk
* @param forceDuck only true if
@@ -1096,7 +1095,7 @@
*/
protected int requestAudioFocus(@NonNull AudioAttributes aa, int focusChangeHint, IBinder cb,
IAudioFocusDispatcher fd, @NonNull String clientId, @NonNull String callingPackageName,
- String attributionTag, int flags, int sdk, boolean forceDuck, int testUid,
+ int flags, int sdk, boolean forceDuck, int testUid,
boolean permissionOverridesCheck) {
new MediaMetrics.Item(mMetricsId)
.setUid(Binder.getCallingUid())
@@ -1129,12 +1128,6 @@
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
- final int res = mAppOps.noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, Binder.getCallingUid(),
- callingPackageName, attributionTag, null);
- if (!permissionOverridesCheck && res != AppOpsManager.MODE_ALLOWED) {
- return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
- }
-
synchronized(mAudioFocusLock) {
// check whether a focus freeze is in place and filter
if (isFocusFrozenForTest()) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index f9f56ee..2660932 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -316,6 +316,8 @@
if (getBiometricContext().isAwake()) {
mALSProbeCallback.getProbe().enable();
+ } else {
+ mALSProbeCallback.getProbe().disable();
}
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
diff --git a/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java b/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java
index e6de14b..16514fa 100644
--- a/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java
+++ b/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java
@@ -29,6 +29,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.util.IndentingPrintWriter;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -121,8 +122,7 @@
+ " without permission " + Manifest.permission.DUMP);
return;
}
- android.util.IndentingPrintWriter radioPrintWriter =
- new android.util.IndentingPrintWriter(printWriter);
+ IndentingPrintWriter radioPrintWriter = new IndentingPrintWriter(printWriter);
radioPrintWriter.printf("BroadcastRadioService\n");
radioPrintWriter.increaseIndent();
diff --git a/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java b/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java
index 93fb7b2..ab08342 100644
--- a/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java
+++ b/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java
@@ -26,6 +26,7 @@
import android.hardware.radio.RadioManager;
import android.os.Binder;
import android.os.RemoteException;
+import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Slog;
@@ -138,7 +139,7 @@
+ " without permission " + Manifest.permission.DUMP);
return;
}
- android.util.IndentingPrintWriter radioPw = new android.util.IndentingPrintWriter(pw);
+ IndentingPrintWriter radioPw = new IndentingPrintWriter(pw);
radioPw.printf("BroadcastRadioService\n");
radioPw.increaseIndent();
diff --git a/services/core/java/com/android/server/broadcastradio/RadioEventLogger.java b/services/core/java/com/android/server/broadcastradio/RadioEventLogger.java
index 2c8f499..b71589c 100644
--- a/services/core/java/com/android/server/broadcastradio/RadioEventLogger.java
+++ b/services/core/java/com/android/server/broadcastradio/RadioEventLogger.java
@@ -17,6 +17,7 @@
package com.android.server.broadcastradio;
import android.text.TextUtils;
+import android.util.IndentingPrintWriter;
import android.util.LocalLog;
import android.util.Log;
@@ -54,7 +55,7 @@
* Dump broadcast radio service event
* @param pw Indenting print writer for dump
*/
- public void dump(android.util.IndentingPrintWriter pw) {
+ public void dump(IndentingPrintWriter pw) {
mEventLogger.dump(pw);
}
}
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/AnnouncementAggregator.java b/services/core/java/com/android/server/broadcastradio/aidl/AnnouncementAggregator.java
index 9654a93..b618aa3 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/AnnouncementAggregator.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/AnnouncementAggregator.java
@@ -22,6 +22,7 @@
import android.hardware.radio.ICloseHandle;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.IndentingPrintWriter;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -93,7 +94,7 @@
if (mCloseHandle != null) mCloseHandle.close();
}
- public void dumpInfo(android.util.IndentingPrintWriter pw) {
+ public void dumpInfo(IndentingPrintWriter pw) {
pw.printf("ModuleWatcher:\n");
pw.increaseIndent();
@@ -191,8 +192,7 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
- android.util.IndentingPrintWriter announcementPrintWriter =
- new android.util.IndentingPrintWriter(printWriter);
+ IndentingPrintWriter announcementPrintWriter = new IndentingPrintWriter(printWriter);
announcementPrintWriter.printf("AnnouncementAggregator\n");
announcementPrintWriter.increaseIndent();
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java b/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java
index 1c42161..d9f8588 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java
@@ -29,6 +29,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.ArrayMap;
+import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.SparseArray;
@@ -128,7 +129,7 @@
if (entry.getValue() == mModuleId) {
Slogf.w(TAG, "Service %s died, removed RadioModule with ID %d",
entry.getKey(), mModuleId);
- return;
+ break;
}
}
}
@@ -260,7 +261,7 @@
*
* @param pw The file to which {@link BroadcastRadioServiceImpl} state is dumped.
*/
- public void dumpInfo(android.util.IndentingPrintWriter pw) {
+ public void dumpInfo(IndentingPrintWriter pw) {
synchronized (mLock) {
pw.printf("Next module id available: %d\n", mNextModuleId);
pw.printf("ServiceName to module id map:\n");
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
index 5b77c52..077e8ee 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
@@ -446,6 +446,7 @@
sel.secondaryIds[i]);
if (id == null) {
Slogf.e(TAG, "invalid secondary id: %s", sel.secondaryIds[i]);
+ continue;
}
secondaryIdList.add(id);
}
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
index 0cac356..03e347a 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
@@ -38,6 +38,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -524,7 +525,7 @@
return BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length);
}
- void dumpInfo(android.util.IndentingPrintWriter pw) {
+ void dumpInfo(IndentingPrintWriter pw) {
pw.printf("RadioModule\n");
pw.increaseIndent();
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java b/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java
index 925f149..e90a1dd 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java
@@ -29,6 +29,7 @@
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
import com.android.internal.annotations.GuardedBy;
import com.android.server.broadcastradio.RadioEventLogger;
@@ -434,7 +435,7 @@
}
}
- void dumpInfo(android.util.IndentingPrintWriter pw) {
+ void dumpInfo(IndentingPrintWriter pw) {
pw.printf("TunerSession\n");
pw.increaseIndent();
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
index e1650c2..a4efa2e 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
@@ -30,6 +30,7 @@
import android.os.IHwBinder.DeathRecipient;
import android.os.RemoteException;
import android.util.ArrayMap;
+import android.util.IndentingPrintWriter;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -115,7 +116,7 @@
if (entry.getValue() == moduleId) {
Slogf.i(TAG, "service " + entry.getKey()
+ " died; removed RadioModule with ID " + moduleId);
- return;
+ break;
}
}
}
@@ -221,7 +222,7 @@
*
* @param pw The file to which BroadcastRadioService state is dumped.
*/
- public void dumpInfo(android.util.IndentingPrintWriter pw) {
+ public void dumpInfo(IndentingPrintWriter pw) {
synchronized (mLock) {
pw.printf("Next module id available: %d\n", mNextModuleId);
pw.printf("ServiceName to module id map:\n");
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
index 34bfa6c..02a9f09 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
@@ -304,10 +304,7 @@
private static boolean isEmpty(
@NonNull android.hardware.broadcastradio.V2_0.ProgramSelector sel) {
- if (sel.primaryId.type != 0) return false;
- if (sel.primaryId.value != 0) return false;
- if (!sel.secondaryIds.isEmpty()) return false;
- return true;
+ return sel.primaryId.type == 0 && sel.primaryId.value == 0 && sel.secondaryIds.isEmpty();
}
static @Nullable ProgramSelector programSelectorFromHal(
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
index 7269f24..d3b2448 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
@@ -40,6 +40,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
import android.util.MutableInt;
import com.android.internal.annotations.GuardedBy;
@@ -453,7 +454,7 @@
return BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length);
}
- void dumpInfo(android.util.IndentingPrintWriter pw) {
+ void dumpInfo(IndentingPrintWriter pw) {
pw.printf("RadioModule\n");
pw.increaseIndent();
pw.printf("BroadcastRadioService: %s\n", mService);
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
index b1b5d34..80efacd 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
@@ -31,6 +31,7 @@
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
import android.util.MutableBoolean;
import android.util.MutableInt;
@@ -324,9 +325,7 @@
try {
isConfigFlagSet(flag);
return true;
- } catch (IllegalStateException ex) {
- return true;
- } catch (UnsupportedOperationException ex) {
+ } catch (IllegalStateException | UnsupportedOperationException ex) {
return false;
}
}
@@ -389,7 +388,7 @@
}
}
- void dumpInfo(android.util.IndentingPrintWriter pw) {
+ void dumpInfo(IndentingPrintWriter pw) {
pw.printf("TunerSession\n");
pw.increaseIndent();
pw.printf("HIDL HAL Session: %s\n", mHwSession);
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 390ee96..17835b2 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -18,6 +18,10 @@
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.os.Build.VERSION_CODES.M;
+import static com.android.internal.util.FrameworkStatsLog.CAMERA_FEATURE_COMBINATION_QUERY_EVENT__STATUS_CODE__OK;
+import static com.android.internal.util.FrameworkStatsLog.CAMERA_FEATURE_COMBINATION_QUERY_EVENT__STATUS_CODE__ERROR_ILLEGAL_ARGUMENT;
+import static com.android.internal.util.FrameworkStatsLog.CAMERA_FEATURE_COMBINATION_QUERY_EVENT__STATUS_CODE__ERROR_INVALID_OPERATION;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -40,6 +44,7 @@
import android.graphics.ImageFormat;
import android.graphics.Rect;
import android.hardware.CameraExtensionSessionStats;
+import android.hardware.CameraFeatureCombinationStats;
import android.hardware.CameraSessionStats;
import android.hardware.CameraStreamStats;
import android.hardware.ICameraService;
@@ -217,7 +222,7 @@
// Map of currently active camera IDs
private final ArrayMap<String, CameraUsageEvent> mActiveCameraUsage = new ArrayMap<>();
- private final List<CameraUsageEvent> mCameraUsageHistory = new ArrayList<>();
+ private final List<CameraEvent> mCameraEventHistory = new ArrayList<CameraEvent>();
private static final String NFC_NOTIFICATION_PROP = "ro.camera.notify_nfc";
private static final IBinder nfcInterfaceToken = new Binder();
@@ -228,9 +233,16 @@
/*corePoolSize*/ 1);
/**
+ * Interface to track camera analytics
+ */
+ private interface CameraEvent {
+ void logSelf();
+ }
+
+ /**
* Structure to track camera usage
*/
- private static class CameraUsageEvent {
+ private static class CameraUsageEvent implements CameraEvent {
public final String mCameraId;
public final int mCameraFacing;
public final String mClientName;
@@ -311,6 +323,214 @@
public long getDuration() {
return mCompleted ? mDurationOrStartTimeMs : 0;
}
+
+ public void logSelf() {
+ int facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__UNKNOWN;
+ switch(mCameraFacing) {
+ case CameraSessionStats.CAMERA_FACING_BACK:
+ facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__BACK;
+ break;
+ case CameraSessionStats.CAMERA_FACING_FRONT:
+ facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__FRONT;
+ break;
+ case CameraSessionStats.CAMERA_FACING_EXTERNAL:
+ facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__EXTERNAL;
+ break;
+ default:
+ Slog.w(TAG, "Unknown camera facing: " + mCameraFacing);
+ }
+
+ int extensionType = FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_NONE;
+ boolean extensionIsAdvanced = false;
+ int extensionCaptureFormat = ImageFormat.UNKNOWN;
+ if (mExtSessionStats != null) {
+ switch (mExtSessionStats.type) {
+ case CameraExtensionSessionStats.Type.EXTENSION_AUTOMATIC:
+ extensionType = FrameworkStatsLog
+ .CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_AUTOMATIC;
+ break;
+ case CameraExtensionSessionStats.Type.EXTENSION_FACE_RETOUCH:
+ extensionType = FrameworkStatsLog
+ .CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_FACE_RETOUCH;
+ break;
+ case CameraExtensionSessionStats.Type.EXTENSION_BOKEH:
+ extensionType =
+ FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_BOKEH;
+ break;
+ case CameraExtensionSessionStats.Type.EXTENSION_HDR:
+ extensionType =
+ FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_HDR;
+ break;
+ case CameraExtensionSessionStats.Type.EXTENSION_NIGHT:
+ extensionType =
+ FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_NIGHT;
+ break;
+ default:
+ Slog.w(TAG, "Unknown extension type: " + mExtSessionStats.type);
+ }
+ extensionIsAdvanced = mExtSessionStats.isAdvanced;
+ if (Flags.analytics24q3()) {
+ extensionCaptureFormat = mExtSessionStats.captureFormat;
+ }
+ }
+
+ int streamCount = 0;
+ if (mStreamStats != null) {
+ streamCount = mStreamStats.size();
+ }
+ if (CameraServiceProxy.DEBUG) {
+ String ultrawideDebug = Flags.logUltrawideUsage()
+ ? ", wideAngleUsage " + mUsedUltraWide
+ : "";
+ String zoomOverrideDebug = Flags.logZoomOverrideUsage()
+ ? ", zoomOverrideUsage " + mUsedZoomOverride
+ : "";
+ String mostRequestedFpsRangeDebug = Flags.analytics24q3()
+ ? ", mostRequestedFpsRange " + mMostRequestedFpsRange
+ : "";
+ String extensionCaptureFormatDebug = Flags.analytics24q3()
+ ? " extensionCaptureFormat " + mExtSessionStats.captureFormat
+ : "";
+
+ Slog.v(TAG, "CAMERA_ACTION_EVENT: action " + mAction
+ + " clientName " + mClientName
+ + ", duration " + getDuration()
+ + ", APILevel " + mAPILevel
+ + ", cameraId " + mCameraId
+ + ", facing " + facing
+ + ", isNdk " + mIsNdk
+ + ", latencyMs " + mLatencyMs
+ + ", operatingMode " + mOperatingMode
+ + ", internalReconfigure " + mInternalReconfigure
+ + ", requestCount " + mRequestCount
+ + ", resultErrorCount " + mResultErrorCount
+ + ", deviceError " + mDeviceError
+ + ", streamCount is " + streamCount
+ + ", userTag is " + mUserTag
+ + ", videoStabilizationMode " + mVideoStabilizationMode
+ + ultrawideDebug
+ + zoomOverrideDebug
+ + mostRequestedFpsRangeDebug
+ + ", logId " + mLogId
+ + ", sessionIndex " + mSessionIndex
+ + ", mExtSessionStats {type " + extensionType
+ + " isAdvanced " + extensionIsAdvanced
+ + extensionCaptureFormatDebug + "}");
+ }
+
+ // Convert from CameraStreamStats to CameraStreamProto
+ CameraStreamProto[] streamProtos = new CameraStreamProto[MAX_STREAM_STATISTICS];
+ for (int i = 0; i < MAX_STREAM_STATISTICS; i++) {
+ streamProtos[i] = new CameraStreamProto();
+ if (i < streamCount) {
+ CameraStreamStats streamStats = mStreamStats.get(i);
+ streamProtos[i].width = streamStats.getWidth();
+ streamProtos[i].height = streamStats.getHeight();
+ streamProtos[i].format = streamStats.getFormat();
+ streamProtos[i].dataSpace = streamStats.getDataSpace();
+ streamProtos[i].usage = streamStats.getUsage();
+ streamProtos[i].requestCount = streamStats.getRequestCount();
+ streamProtos[i].errorCount = streamStats.getErrorCount();
+ streamProtos[i].firstCaptureLatencyMillis = streamStats.getStartLatencyMs();
+ streamProtos[i].maxHalBuffers = streamStats.getMaxHalBuffers();
+ streamProtos[i].maxAppBuffers = streamStats.getMaxAppBuffers();
+ streamProtos[i].histogramType = streamStats.getHistogramType();
+ streamProtos[i].histogramBins = streamStats.getHistogramBins();
+ streamProtos[i].histogramCounts = streamStats.getHistogramCounts();
+ streamProtos[i].dynamicRangeProfile = streamStats.getDynamicRangeProfile();
+ streamProtos[i].streamUseCase = streamStats.getStreamUseCase();
+ streamProtos[i].colorSpace = streamStats.getColorSpace();
+
+ if (CameraServiceProxy.DEBUG) {
+ String histogramTypeName =
+ cameraHistogramTypeToString(streamProtos[i].histogramType);
+ Slog.v(TAG, "Stream " + i + ": width " + streamProtos[i].width
+ + ", height " + streamProtos[i].height
+ + ", format " + streamProtos[i].format
+ + ", maxPreviewFps " + streamStats.getMaxPreviewFps()
+ + ", dataSpace " + streamProtos[i].dataSpace
+ + ", usage " + streamProtos[i].usage
+ + ", requestCount " + streamProtos[i].requestCount
+ + ", errorCount " + streamProtos[i].errorCount
+ + ", firstCaptureLatencyMillis "
+ + streamProtos[i].firstCaptureLatencyMillis
+ + ", maxHalBuffers " + streamProtos[i].maxHalBuffers
+ + ", maxAppBuffers " + streamProtos[i].maxAppBuffers
+ + ", histogramType " + histogramTypeName
+ + ", histogramBins "
+ + Arrays.toString(streamProtos[i].histogramBins)
+ + ", histogramCounts "
+ + Arrays.toString(streamProtos[i].histogramCounts)
+ + ", dynamicRangeProfile " + streamProtos[i].dynamicRangeProfile
+ + ", streamUseCase " + streamProtos[i].streamUseCase
+ + ", colorSpace " + streamProtos[i].colorSpace);
+ }
+ }
+ }
+ FrameworkStatsLog.write(FrameworkStatsLog.CAMERA_ACTION_EVENT, getDuration(),
+ mAPILevel, mClientName, facing, mCameraId, mAction, mIsNdk,
+ mLatencyMs, mOperatingMode, mInternalReconfigure,
+ mRequestCount, mResultErrorCount, mDeviceError,
+ streamCount, MessageNano.toByteArray(streamProtos[0]),
+ MessageNano.toByteArray(streamProtos[1]),
+ MessageNano.toByteArray(streamProtos[2]),
+ MessageNano.toByteArray(streamProtos[3]),
+ MessageNano.toByteArray(streamProtos[4]),
+ mUserTag, mVideoStabilizationMode,
+ mLogId, mSessionIndex,
+ extensionType, extensionIsAdvanced, mUsedUltraWide,
+ mUsedZoomOverride,
+ mMostRequestedFpsRange.getLower(), mMostRequestedFpsRange.getUpper(),
+ extensionCaptureFormat);
+
+ }
+ }
+ /**
+ * Structure to track feature combination query
+ */
+ private static class CameraFeatureCombinationQueryEvent implements CameraEvent {
+ private CameraFeatureCombinationStats mFeatureCombinationStats;
+
+ CameraFeatureCombinationQueryEvent(CameraFeatureCombinationStats featureCombinationStats) {
+ mFeatureCombinationStats = featureCombinationStats;
+ }
+
+ public void logSelf() {
+ int statusCode = -1;
+ switch (mFeatureCombinationStats.mStatus) {
+ case 0:
+ statusCode = CAMERA_FEATURE_COMBINATION_QUERY_EVENT__STATUS_CODE__OK;
+ break;
+ case ICameraService.ERROR_ILLEGAL_ARGUMENT:
+ statusCode = CAMERA_FEATURE_COMBINATION_QUERY_EVENT__STATUS_CODE__ERROR_ILLEGAL_ARGUMENT;
+ break;
+ case ICameraService.ERROR_INVALID_OPERATION:
+ statusCode = CAMERA_FEATURE_COMBINATION_QUERY_EVENT__STATUS_CODE__ERROR_INVALID_OPERATION;
+ break;
+ default:
+ break;
+ }
+ if (statusCode == -1) {
+ Slog.w(TAG, "Unknown feature combination query status code: "
+ + mFeatureCombinationStats.mStatus);
+ return;
+ }
+
+ if (CameraServiceProxy.DEBUG) {
+ Slog.v(TAG, "CAMERA_FEATURE_COMBINATION_QUERY_EVENT: uid "
+ + mFeatureCombinationStats.mUid
+ + ", cameraId " + mFeatureCombinationStats.mCameraId
+ + ", queryType " + mFeatureCombinationStats.mQueryType
+ + ", featureCombination " + mFeatureCombinationStats.mFeatureCombination
+ + ", status " + statusCode);
+ }
+ FrameworkStatsLog.write(FrameworkStatsLog.CAMERA_FEATURE_COMBINATION_QUERY_EVENT,
+ mFeatureCombinationStats.mUid,
+ mFeatureCombinationStats.mCameraId,
+ mFeatureCombinationStats.mQueryType,
+ mFeatureCombinationStats.mFeatureCombination,
+ statusCode);
+ }
}
private final class DisplayWindowListener extends IDisplayWindowListener.Stub {
@@ -625,6 +845,32 @@
}
@Override
+ public void notifyFeatureCombinationStats(CameraFeatureCombinationStats featureCombStats) {
+ if (!Flags.analytics24q3()) {
+ return;
+ }
+ if (Binder.getCallingUid() != Process.CAMERASERVER_UID) {
+ Slog.e(TAG, "Calling UID: " + Binder.getCallingUid() + " doesn't match expected "
+ + " camera service UID!");
+ return;
+ }
+
+ if (DEBUG) {
+ String combinationTypeStr = cameraFeatureCombinationTypeToString(
+ featureCombStats.mQueryType);
+ String combinationStr = cameraFeatureCombinationToString(
+ featureCombStats.mFeatureCombination);
+ Slog.v(TAG, "Camera " + featureCombStats.mCameraId
+ + " query " + combinationTypeStr
+ + " combination " + combinationStr
+ + " for client UID " + featureCombStats.mUid
+ + " returns " + featureCombStats.mStatus);
+ }
+
+ updateFeatureCombinationQuery(featureCombStats);
+ }
+
+ @Override
public boolean isCameraDisabled(int userId) {
if (Binder.getCallingUid() != Process.CAMERASERVER_UID) {
Slog.e(TAG, "Calling UID: " + Binder.getCallingUid()
@@ -684,7 +930,7 @@
switch (cmd.replace('-', '_')) {
case "dump_events":
int eventCount = mCameraServiceProxy.getUsageEventCount();
- mCameraServiceProxy.dumpUsageEvents();
+ mCameraServiceProxy.dumpCameraEvents();
pw.println("Camera usage events dumped: " + eventCount);
break;
default:
@@ -865,18 +1111,18 @@
}
private class EventWriterTask implements Runnable {
- private ArrayList<CameraUsageEvent> mEventList;
+ private List<CameraEvent> mEventList;
private static final long WRITER_SLEEP_MS = 100;
- public EventWriterTask(ArrayList<CameraUsageEvent> eventList) {
+ EventWriterTask(List<CameraEvent> eventList) {
mEventList = eventList;
}
@Override
public void run() {
if (mEventList != null) {
- for (CameraUsageEvent event : mEventList) {
- logCameraUsageEvent(event);
+ for (CameraEvent event : mEventList) {
+ event.logSelf();
try {
Thread.sleep(WRITER_SLEEP_MS);
} catch (InterruptedException e) {}
@@ -884,170 +1130,6 @@
mEventList.clear();
}
}
-
- /**
- * Write camera usage events to stats log.
- * Package-private
- */
- private void logCameraUsageEvent(CameraUsageEvent e) {
- int facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__UNKNOWN;
- switch(e.mCameraFacing) {
- case CameraSessionStats.CAMERA_FACING_BACK:
- facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__BACK;
- break;
- case CameraSessionStats.CAMERA_FACING_FRONT:
- facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__FRONT;
- break;
- case CameraSessionStats.CAMERA_FACING_EXTERNAL:
- facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__EXTERNAL;
- break;
- default:
- Slog.w(TAG, "Unknown camera facing: " + e.mCameraFacing);
- }
-
- int extensionType = FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_NONE;
- boolean extensionIsAdvanced = false;
- int extensionCaptureFormat = ImageFormat.UNKNOWN;
- if (e.mExtSessionStats != null) {
- switch (e.mExtSessionStats.type) {
- case CameraExtensionSessionStats.Type.EXTENSION_AUTOMATIC:
- extensionType = FrameworkStatsLog
- .CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_AUTOMATIC;
- break;
- case CameraExtensionSessionStats.Type.EXTENSION_FACE_RETOUCH:
- extensionType = FrameworkStatsLog
- .CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_FACE_RETOUCH;
- break;
- case CameraExtensionSessionStats.Type.EXTENSION_BOKEH:
- extensionType =
- FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_BOKEH;
- break;
- case CameraExtensionSessionStats.Type.EXTENSION_HDR:
- extensionType =
- FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_HDR;
- break;
- case CameraExtensionSessionStats.Type.EXTENSION_NIGHT:
- extensionType =
- FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_NIGHT;
- break;
- default:
- Slog.w(TAG, "Unknown extension type: " + e.mExtSessionStats.type);
- }
- extensionIsAdvanced = e.mExtSessionStats.isAdvanced;
- if (Flags.analytics24q3()) {
- extensionCaptureFormat = e.mExtSessionStats.captureFormat;
- }
- }
-
- int streamCount = 0;
- if (e.mStreamStats != null) {
- streamCount = e.mStreamStats.size();
- }
- if (CameraServiceProxy.DEBUG) {
- String ultrawideDebug = Flags.logUltrawideUsage()
- ? ", wideAngleUsage " + e.mUsedUltraWide
- : "";
- String zoomOverrideDebug = Flags.logZoomOverrideUsage()
- ? ", zoomOverrideUsage " + e.mUsedZoomOverride
- : "";
- String mostRequestedFpsRangeDebug = Flags.analytics24q3()
- ? ", mostRequestedFpsRange " + e.mMostRequestedFpsRange
- : "";
- String extensionCaptureFormatDebug = Flags.analytics24q3()
- ? " extensionCaptureFormat " + e.mExtSessionStats.captureFormat
- : "";
-
- Slog.v(TAG, "CAMERA_ACTION_EVENT: action " + e.mAction
- + " clientName " + e.mClientName
- + ", duration " + e.getDuration()
- + ", APILevel " + e.mAPILevel
- + ", cameraId " + e.mCameraId
- + ", facing " + facing
- + ", isNdk " + e.mIsNdk
- + ", latencyMs " + e.mLatencyMs
- + ", operatingMode " + e.mOperatingMode
- + ", internalReconfigure " + e.mInternalReconfigure
- + ", requestCount " + e.mRequestCount
- + ", resultErrorCount " + e.mResultErrorCount
- + ", deviceError " + e.mDeviceError
- + ", streamCount is " + streamCount
- + ", userTag is " + e.mUserTag
- + ", videoStabilizationMode " + e.mVideoStabilizationMode
- + ultrawideDebug
- + zoomOverrideDebug
- + mostRequestedFpsRangeDebug
- + ", logId " + e.mLogId
- + ", sessionIndex " + e.mSessionIndex
- + ", mExtSessionStats {type " + extensionType
- + " isAdvanced " + extensionIsAdvanced
- + extensionCaptureFormatDebug + "}");
- }
-
- // Convert from CameraStreamStats to CameraStreamProto
- CameraStreamProto[] streamProtos = new CameraStreamProto[MAX_STREAM_STATISTICS];
- for (int i = 0; i < MAX_STREAM_STATISTICS; i++) {
- streamProtos[i] = new CameraStreamProto();
- if (i < streamCount) {
- CameraStreamStats streamStats = e.mStreamStats.get(i);
- streamProtos[i].width = streamStats.getWidth();
- streamProtos[i].height = streamStats.getHeight();
- streamProtos[i].format = streamStats.getFormat();
- streamProtos[i].dataSpace = streamStats.getDataSpace();
- streamProtos[i].usage = streamStats.getUsage();
- streamProtos[i].requestCount = streamStats.getRequestCount();
- streamProtos[i].errorCount = streamStats.getErrorCount();
- streamProtos[i].firstCaptureLatencyMillis = streamStats.getStartLatencyMs();
- streamProtos[i].maxHalBuffers = streamStats.getMaxHalBuffers();
- streamProtos[i].maxAppBuffers = streamStats.getMaxAppBuffers();
- streamProtos[i].histogramType = streamStats.getHistogramType();
- streamProtos[i].histogramBins = streamStats.getHistogramBins();
- streamProtos[i].histogramCounts = streamStats.getHistogramCounts();
- streamProtos[i].dynamicRangeProfile = streamStats.getDynamicRangeProfile();
- streamProtos[i].streamUseCase = streamStats.getStreamUseCase();
- streamProtos[i].colorSpace = streamStats.getColorSpace();
-
- if (CameraServiceProxy.DEBUG) {
- String histogramTypeName =
- cameraHistogramTypeToString(streamProtos[i].histogramType);
- Slog.v(TAG, "Stream " + i + ": width " + streamProtos[i].width
- + ", height " + streamProtos[i].height
- + ", format " + streamProtos[i].format
- + ", maxPreviewFps " + streamStats.getMaxPreviewFps()
- + ", dataSpace " + streamProtos[i].dataSpace
- + ", usage " + streamProtos[i].usage
- + ", requestCount " + streamProtos[i].requestCount
- + ", errorCount " + streamProtos[i].errorCount
- + ", firstCaptureLatencyMillis "
- + streamProtos[i].firstCaptureLatencyMillis
- + ", maxHalBuffers " + streamProtos[i].maxHalBuffers
- + ", maxAppBuffers " + streamProtos[i].maxAppBuffers
- + ", histogramType " + histogramTypeName
- + ", histogramBins "
- + Arrays.toString(streamProtos[i].histogramBins)
- + ", histogramCounts "
- + Arrays.toString(streamProtos[i].histogramCounts)
- + ", dynamicRangeProfile " + streamProtos[i].dynamicRangeProfile
- + ", streamUseCase " + streamProtos[i].streamUseCase
- + ", colorSpace " + streamProtos[i].colorSpace);
- }
- }
- }
- FrameworkStatsLog.write(FrameworkStatsLog.CAMERA_ACTION_EVENT, e.getDuration(),
- e.mAPILevel, e.mClientName, facing, e.mCameraId, e.mAction, e.mIsNdk,
- e.mLatencyMs, e.mOperatingMode, e.mInternalReconfigure,
- e.mRequestCount, e.mResultErrorCount, e.mDeviceError,
- streamCount, MessageNano.toByteArray(streamProtos[0]),
- MessageNano.toByteArray(streamProtos[1]),
- MessageNano.toByteArray(streamProtos[2]),
- MessageNano.toByteArray(streamProtos[3]),
- MessageNano.toByteArray(streamProtos[4]),
- e.mUserTag, e.mVideoStabilizationMode,
- e.mLogId, e.mSessionIndex,
- extensionType, extensionIsAdvanced, e.mUsedUltraWide,
- e.mUsedZoomOverride,
- e.mMostRequestedFpsRange.getLower(), e.mMostRequestedFpsRange.getUpper(),
- extensionCaptureFormat);
- }
}
/**
@@ -1055,22 +1137,22 @@
*/
int getUsageEventCount() {
synchronized (mLock) {
- return mCameraUsageHistory.size();
+ return mCameraEventHistory.size();
}
}
/**
- * Dump camera usage events to log.
+ * Dump camera events to log.
* Package-private
*/
- void dumpUsageEvents() {
+ void dumpCameraEvents() {
synchronized(mLock) {
// Randomize order of events so that it's not meaningful
- Collections.shuffle(mCameraUsageHistory);
+ Collections.shuffle(mCameraEventHistory);
mLogWriterService.execute(new EventWriterTask(
- new ArrayList<CameraUsageEvent>(mCameraUsageHistory)));
+ new ArrayList(mCameraEventHistory)));
- mCameraUsageHistory.clear();
+ mCameraEventHistory.clear();
}
final long ident = Binder.clearCallingIdentity();
try {
@@ -1288,7 +1370,7 @@
cameraId, facing, clientName, apiLevel, isNdk,
FrameworkStatsLog.CAMERA_ACTION_EVENT__ACTION__OPEN,
latencyMs, sessionType, deviceError, logId, sessionIdx);
- mCameraUsageHistory.add(openEvent);
+ mCameraEventHistory.add(openEvent);
break;
case CameraSessionStats.CAMERA_STATE_ACTIVE:
// Check current active camera IDs to see if this package is already talking to
@@ -1323,7 +1405,7 @@
/*userTag*/"", /*videoStabilizationMode*/-1, /*usedUltraWide*/false,
/*usedZoomOverride*/false, new Range<Integer>(0, 0),
new CameraExtensionSessionStats());
- mCameraUsageHistory.add(oldEvent);
+ mCameraEventHistory.add(oldEvent);
}
break;
case CameraSessionStats.CAMERA_STATE_IDLE:
@@ -1335,7 +1417,7 @@
resultErrorCount, deviceError, streamStats, userTag,
videoStabilizationMode, usedUltraWide, usedZoomOverride,
mostRequestedFpsRange, extSessionStats);
- mCameraUsageHistory.add(doneEvent);
+ mCameraEventHistory.add(doneEvent);
// Do not double count device error
deviceError = false;
@@ -1362,11 +1444,11 @@
cameraId, facing, clientName, apiLevel, isNdk,
FrameworkStatsLog.CAMERA_ACTION_EVENT__ACTION__CLOSE,
latencyMs, sessionType, deviceError, logId, sessionIdx);
- mCameraUsageHistory.add(closeEvent);
+ mCameraEventHistory.add(closeEvent);
}
- if (mCameraUsageHistory.size() > MAX_USAGE_HISTORY) {
- dumpUsageEvents();
+ if (mCameraEventHistory.size() > MAX_USAGE_HISTORY) {
+ dumpCameraEvents();
}
break;
@@ -1378,6 +1460,18 @@
}
}
+ private void updateFeatureCombinationQuery(CameraFeatureCombinationStats featureCombStats) {
+ synchronized (mLock) {
+ CameraFeatureCombinationQueryEvent e =
+ new CameraFeatureCombinationQueryEvent(featureCombStats);
+ mCameraEventHistory.add(e);
+
+ if (mCameraEventHistory.size() > MAX_USAGE_HISTORY) {
+ dumpCameraEvents();
+ }
+ }
+ }
+
private void notifyNfcService(boolean enablePolling) {
NfcManager nfcManager = mContext.getSystemService(NfcManager.class);
if (nfcManager == null) {
@@ -1434,4 +1528,41 @@
return "HISTOGRAM_TYPE_UNKNOWN";
}
+ private static String cameraFeatureCombinationTypeToString(int featureCombinationType) {
+ switch (featureCombinationType) {
+ case CameraFeatureCombinationStats.QueryType.QUERY_FEATURE_COMBINATION:
+ return "QUERY_FEATURE_COMBINATION";
+ case CameraFeatureCombinationStats.QueryType.QUERY_SESSION_CHARACTERISTICS:
+ return "QUERY_SESSION_CHARACTERISTICS";
+ default:
+ break;
+ }
+ return "FEATURE_COMBINATION_TYPE_UNKNOWN";
+ }
+
+ private static String cameraFeatureCombinationToString(long featureCombination) {
+ StringBuilder combinationStr = new StringBuilder("{");
+ if ((featureCombination & CameraFeatureCombinationStats.CAMERA_FEATURE_60_FPS) != 0) {
+ combinationStr.append("60fps ");
+ }
+ if ((featureCombination & CameraFeatureCombinationStats.CAMERA_FEATURE_STABILIZATION)
+ != 0) {
+ combinationStr.append("stabilization ");
+ }
+ if ((featureCombination & CameraFeatureCombinationStats.CAMERA_FEATURE_HLG10) != 0) {
+ combinationStr.append("hlg10 ");
+ }
+ if ((featureCombination & CameraFeatureCombinationStats.CAMERA_FEATURE_JPEG) != 0) {
+ combinationStr.append("jpeg ");
+ }
+ if ((featureCombination & CameraFeatureCombinationStats.CAMERA_FEATURE_JPEG_R) != 0) {
+ combinationStr.append("jpeg_r ");
+ }
+ if ((featureCombination & CameraFeatureCombinationStats.CAMERA_FEATURE_4K) != 0) {
+ combinationStr.append("4k ");
+ }
+ combinationStr.append("}");
+
+ return combinationStr.toString();
+ }
}
diff --git a/services/core/java/com/android/server/camera/CameraStatsJobService.java b/services/core/java/com/android/server/camera/CameraStatsJobService.java
index b8a6846..1227ca7 100644
--- a/services/core/java/com/android/server/camera/CameraStatsJobService.java
+++ b/services/core/java/com/android/server/camera/CameraStatsJobService.java
@@ -21,14 +21,13 @@
import android.app.job.JobScheduler;
import android.app.job.JobService;
import android.content.ComponentName;
-import android.content.ContentResolver;
import android.content.Context;
import android.util.Slog;
-import java.util.concurrent.TimeUnit;
-
import com.android.server.LocalServices;
+import java.util.concurrent.TimeUnit;
+
/**
* A JobService to periodically collect camera usage stats.
*/
@@ -50,7 +49,7 @@
return false;
}
- serviceProxy.dumpUsageEvents();
+ serviceProxy.dumpCameraEvents();
return false;
}
diff --git a/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java b/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java
index 0812fd9..de7341d 100644
--- a/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java
+++ b/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java
@@ -21,6 +21,7 @@
import android.app.assist.ActivityId;
import android.content.ComponentName;
import android.content.ContentCaptureOptions;
+import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.service.contentcapture.ActivityEvent.ActivityEventType;
@@ -40,6 +41,14 @@
public abstract boolean isContentCaptureServiceForUser(int uid, @UserIdInt int userId);
/**
+ * Notifies the intelligence service of new intent data associated with an activity start event.
+ *
+ * @return {@code false} if there was no service set for the given user
+ */
+ public abstract boolean sendActivityStartAssistData(@UserIdInt int userId,
+ @NonNull IBinder activityToken, @NonNull Intent intentData);
+
+ /**
* Notifies the intelligence service of new assist data for the given activity.
*
* @return {@code false} if there was no service set for the given user
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 1949e6f..7b5cff7 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -54,7 +54,6 @@
import com.android.internal.os.BackgroundThread;
import com.android.server.EventLogTags;
import com.android.server.display.brightness.BrightnessEvent;
-import com.android.server.display.brightness.clamper.BrightnessClamperController;
import com.android.server.display.config.HysteresisLevels;
import com.android.server.display.feature.DisplayManagerFlags;
@@ -256,7 +255,6 @@
// Controls Brightness range (including High Brightness Mode).
private final BrightnessRangeController mBrightnessRangeController;
- private final BrightnessClamperController mBrightnessClamperController;
// Throttles (caps) maximum allowed brightness
private final BrightnessThrottler mBrightnessThrottler;
@@ -295,7 +293,6 @@
BrightnessRangeController brightnessModeController,
BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort,
int ambientLightHorizonLong, float userLux, float userNits,
- BrightnessClamperController brightnessClamperController,
DisplayManagerFlags displayManagerFlags) {
this(new Injector(), callbacks, looper, sensorManager, lightSensor,
brightnessMappingStrategyMap, lightSensorWarmUpTime, brightnessMin, brightnessMax,
@@ -306,7 +303,7 @@
screenBrightnessThresholds, ambientBrightnessThresholdsIdle,
screenBrightnessThresholdsIdle, context, brightnessModeController,
brightnessThrottler, ambientLightHorizonShort, ambientLightHorizonLong, userLux,
- userNits, brightnessClamperController, displayManagerFlags
+ userNits, displayManagerFlags
);
}
@@ -325,7 +322,6 @@
BrightnessRangeController brightnessRangeController,
BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort,
int ambientLightHorizonLong, float userLux, float userNits,
- BrightnessClamperController brightnessClamperController,
DisplayManagerFlags displayManagerFlags) {
mInjector = injector;
mClock = injector.createClock(displayManagerFlags.offloadControlsDozeAutoBrightness());
@@ -370,7 +366,6 @@
mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
mBrightnessRangeController = brightnessRangeController;
- mBrightnessClamperController = brightnessClamperController;
mBrightnessThrottler = brightnessThrottler;
mBrightnessMappingStrategyMap = brightnessMappingStrategyMap;
mDisplayManagerFlags = displayManagerFlags;
@@ -771,7 +766,6 @@
mAmbientBrightnessThresholds.getDarkeningThreshold(lux);
}
mBrightnessRangeController.onAmbientLuxChange(mAmbientLux);
- mBrightnessClamperController.onAmbientLuxChange(mAmbientLux);
// If the short term model was invalidated and the change is drastic enough, reset it.
mShortTermModel.maybeReset(mAmbientLux);
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index b1b1dba..93bd926 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -212,24 +212,46 @@
public static final int TOUCH_VIRTUAL = 3;
/**
- * Diff result: The {@link #state} or {@link #committedState} fields differ.
- */
- public static final int DIFF_STATE = 1 << 0;
-
- /**
* Diff result: Other fields differ.
*/
- public static final int DIFF_OTHER = 1 << 1;
+ public static final int DIFF_OTHER = 1 << 0;
+
+ /**
+ * Diff result: The {@link #state} or {@link #committedState} fields differ.
+ */
+ public static final int DIFF_STATE = 1 << 1;
+
+ /**
+ * Diff result: The committed state differs. Note this is slightly different from the state,
+ * which is what most of the device should care about.
+ */
+ public static final int DIFF_COMMITTED_STATE = 1 << 2;
/**
* Diff result: The color mode fields differ.
*/
- public static final int DIFF_COLOR_MODE = 1 << 2;
+ public static final int DIFF_COLOR_MODE = 1 << 3;
/**
* Diff result: The hdr/sdr ratio differs
*/
- public static final int DIFF_HDR_SDR_RATIO = 1 << 3;
+ public static final int DIFF_HDR_SDR_RATIO = 1 << 4;
+
+ /**
+ * Diff result: The rotation differs
+ */
+ public static final int DIFF_ROTATION = 1 << 5;
+
+ /**
+ * Diff result: The render timings. Note this could be any of {@link #renderFrameRate},
+ * {@link #presentationDeadlineNanos}, or {@link #appVsyncOffsetNanos}.
+ */
+ public static final int DIFF_RENDER_TIMINGS = 1 << 6;
+
+ /**
+ * Diff result: The mode ID differs.
+ */
+ public static final int DIFF_MODE_ID = 1 << 7;
/**
* Diff result: Catch-all for "everything changed"
@@ -462,21 +484,33 @@
*/
public int diff(DisplayDeviceInfo other) {
int diff = 0;
- if (state != other.state || committedState != other.committedState) {
+ if (state != other.state) {
diff |= DIFF_STATE;
}
+ if (committedState != other.committedState) {
+ diff |= DIFF_COMMITTED_STATE;
+ }
if (colorMode != other.colorMode) {
diff |= DIFF_COLOR_MODE;
}
if (!BrightnessSynchronizer.floatEquals(hdrSdrRatio, other.hdrSdrRatio)) {
diff |= DIFF_HDR_SDR_RATIO;
}
+ if (rotation != other.rotation) {
+ diff |= DIFF_ROTATION;
+ }
+ if (renderFrameRate != other.renderFrameRate
+ || presentationDeadlineNanos != other.presentationDeadlineNanos
+ || appVsyncOffsetNanos != other.appVsyncOffsetNanos) {
+ diff |= DIFF_RENDER_TIMINGS;
+ }
+ if (modeId != other.modeId) {
+ diff |= DIFF_MODE_ID;
+ }
if (!Objects.equals(name, other.name)
|| !Objects.equals(uniqueId, other.uniqueId)
|| width != other.width
|| height != other.height
- || modeId != other.modeId
- || renderFrameRate != other.renderFrameRate
|| defaultModeId != other.defaultModeId
|| userPreferredModeId != other.userPreferredModeId
|| !Arrays.equals(supportedModes, other.supportedModes)
@@ -487,12 +521,9 @@
|| densityDpi != other.densityDpi
|| xDpi != other.xDpi
|| yDpi != other.yDpi
- || appVsyncOffsetNanos != other.appVsyncOffsetNanos
- || presentationDeadlineNanos != other.presentationDeadlineNanos
|| flags != other.flags
|| !Objects.equals(displayCutout, other.displayCutout)
|| touch != other.touch
- || rotation != other.rotation
|| type != other.type
|| !Objects.equals(address, other.address)
|| !Objects.equals(deviceProductInfo, other.deviceProductInfo)
diff --git a/services/core/java/com/android/server/display/DisplayDeviceRepository.java b/services/core/java/com/android/server/display/DisplayDeviceRepository.java
index 6164154..086f8a9 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceRepository.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceRepository.java
@@ -21,6 +21,7 @@
import android.util.Slog;
import android.view.Display;
import android.view.DisplayAddress;
+import android.view.Surface;
import com.android.internal.annotations.GuardedBy;
import com.android.server.display.DisplayManagerService.SyncRoot;
@@ -179,6 +180,20 @@
if (diff == DisplayDeviceInfo.DIFF_STATE) {
Slog.i(TAG, "Display device changed state: \"" + info.name
+ "\", " + Display.stateToString(info.state));
+ } else if (diff == DisplayDeviceInfo.DIFF_ROTATION) {
+ Slog.i(TAG, "Display device rotated: \"" + info.name
+ + "\", " + Surface.rotationToString(info.rotation));
+ } else if (diff
+ == (DisplayDeviceInfo.DIFF_MODE_ID | DisplayDeviceInfo.DIFF_RENDER_TIMINGS)) {
+ Slog.i(TAG, "Display device changed render timings: \"" + info.name
+ + "\", renderFrameRate=" + info.renderFrameRate
+ + ", presentationDeadlineNanos=" + info.presentationDeadlineNanos
+ + ", appVsyncOffsetNanos=" + info.appVsyncOffsetNanos);
+ } else if (diff == DisplayDeviceInfo.DIFF_COMMITTED_STATE) {
+ if (DEBUG) {
+ Slog.i(TAG, "Display device changed committed state: \"" + info.name
+ + "\", " + Display.stateToString(info.committedState));
+ }
} else if (diff != DisplayDeviceInfo.DIFF_HDR_SDR_RATIO) {
Slog.i(TAG, "Display device changed: " + info);
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 5fd0253..2d5f38e 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -3406,6 +3406,31 @@
}
}
+ boolean requestDisplayPower(int displayId, boolean on) {
+ synchronized (mSyncRoot) {
+ final var display = mLogicalDisplayMapper.getDisplayLocked(displayId);
+ if (display == null) {
+ Slog.w(TAG, "requestDisplayPower: Cannot find a display with displayId="
+ + displayId);
+ return false;
+ }
+ final BrightnessPair brightnessPair = mDisplayBrightnesses.get(displayId);
+ var runnable = display.getPrimaryDisplayDeviceLocked().requestDisplayStateLocked(
+ on ? Display.STATE_ON : Display.STATE_OFF,
+ on ? brightnessPair.brightness : PowerManager.BRIGHTNESS_OFF_FLOAT,
+ brightnessPair.sdrBrightness,
+ display.getDisplayOffloadSessionLocked());
+ if (runnable == null) {
+ Slog.w(TAG, "requestDisplayPower: Cannot update the power state to ON=" + on
+ + " for a display with displayId=" + displayId + ", runnable is null");
+ return false;
+ }
+ runnable.run();
+ Slog.i(TAG, "requestDisplayPower(displayId=" + displayId + ", on=" + on + ")");
+ }
+ return true;
+ }
+
/**
* This is the object that everything in the display manager locks on.
* We make it an inner class within the {@link DisplayManagerService} to so that it is
@@ -4629,6 +4654,12 @@
DisplayManagerService.this.enableConnectedDisplay(displayId, false);
}
+ @EnforcePermission(MANAGE_DISPLAYS)
+ public boolean requestDisplayPower(int displayId, boolean on) {
+ requestDisplayPower_enforcePermission();
+ return DisplayManagerService.this.requestDisplayPower(displayId, on);
+ }
+
@EnforcePermission(RESTRICT_DISPLAY_MODES)
@Override // Binder call
public void requestDisplayModes(IBinder token, int displayId, @Nullable int[] modeIds) {
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index 8c39d7d..d973b71 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -106,6 +106,10 @@
return setDisplayEnabled(true);
case "disable-display":
return setDisplayEnabled(false);
+ case "power-on":
+ return requestDisplayPower(true);
+ case "power-off":
+ return requestDisplayPower(false);
default:
return handleDefaultCommands(cmd);
}
@@ -592,4 +596,21 @@
mService.enableConnectedDisplay(displayId, enable);
return 0;
}
+
+ private int requestDisplayPower(boolean enable) {
+ final String displayIdText = getNextArg();
+ if (displayIdText == null) {
+ getErrPrintWriter().println("Error: no displayId specified");
+ return 1;
+ }
+ final int displayId;
+ try {
+ displayId = Integer.parseInt(displayIdText);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: invalid displayId: '" + displayIdText + "'");
+ return 1;
+ }
+ mService.requestDisplayPower(displayId, enable);
+ return 0;
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 195a516..65a729a 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -587,7 +587,7 @@
mUniqueDisplayId,
mThermalBrightnessThrottlingDataId,
logicalDisplay.getPowerThrottlingDataIdLocked(),
- mDisplayDeviceConfig), mContext, flags);
+ mDisplayDeviceConfig), mContext, flags, mSensorManager);
// Seed the cached brightness
saveBrightnessInfo(getScreenBrightnessSetting());
mAutomaticBrightnessStrategy =
@@ -1422,7 +1422,6 @@
: AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
mBrightnessRangeController.setAutoBrightnessEnabled(autoBrightnessState);
- mBrightnessClamperController.setAutoBrightnessState(autoBrightnessState);
boolean updateScreenBrightnessSetting =
displayBrightnessState.shouldUpdateScreenBrightnessSetting();
@@ -1549,7 +1548,7 @@
// we broadcast this change through setting.
final float unthrottledBrightnessState = brightnessState;
DisplayBrightnessState clampedState = mBrightnessClamperController.clamp(mPowerRequest,
- brightnessState, slowChange);
+ brightnessState, slowChange, /* displayState= */ state);
brightnessState = clampedState.getBrightness();
slowChange = clampedState.isSlowChange();
@@ -2478,7 +2477,6 @@
public void setBrightnessToFollow(float leadDisplayBrightness, float nits, float ambientLux,
boolean slowChange) {
mBrightnessRangeController.onAmbientLuxChange(ambientLux);
- mBrightnessClamperController.onAmbientLuxChange(ambientLux);
if (nits == BrightnessMappingStrategy.INVALID_NITS) {
mDisplayBrightnessController.setBrightnessToFollow(leadDisplayBrightness, slowChange);
} else {
@@ -3194,7 +3192,7 @@
screenBrightnessThresholds, ambientBrightnessThresholdsIdle,
screenBrightnessThresholdsIdle, context, brightnessModeController,
brightnessThrottler, ambientLightHorizonShort, ambientLightHorizonLong, userLux,
- userNits, brightnessClamperController, displayManagerFlags);
+ userNits, displayManagerFlags);
}
BrightnessMappingStrategy getDefaultModeBrightnessMapper(Context context,
@@ -3243,10 +3241,10 @@
BrightnessClamperController getBrightnessClamperController(Handler handler,
BrightnessClamperController.ClamperChangeListener clamperChangeListener,
BrightnessClamperController.DisplayDeviceData data, Context context,
- DisplayManagerFlags flags) {
+ DisplayManagerFlags flags, SensorManager sensorManager) {
return new BrightnessClamperController(handler, clamperChangeListener, data, context,
- flags);
+ flags, sensorManager);
}
DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler,
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
index 11ef577..101ad30 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
@@ -16,21 +16,30 @@
package com.android.server.display.brightness.clamper;
+import static android.view.Display.STATE_ON;
+
import static com.android.server.display.brightness.clamper.BrightnessClamper.Type;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.res.Resources;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
import android.hardware.display.BrightnessInfo;
import android.hardware.display.DisplayManagerInternal;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.PowerManager;
+import android.os.SystemClock;
import android.provider.DeviceConfig;
import android.provider.DeviceConfigInterface;
import android.util.IndentingPrintWriter;
import android.util.Slog;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.display.DisplayBrightnessState;
import com.android.server.display.DisplayDeviceConfig;
@@ -41,20 +50,30 @@
import com.android.server.display.config.SensorData;
import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.display.feature.DisplayManagerFlags;
+import com.android.server.display.utils.AmbientFilter;
+import com.android.server.display.utils.AmbientFilterFactory;
+import com.android.server.display.utils.DebugUtils;
+import com.android.server.display.utils.SensorUtils;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
/**
* Clampers controller, all in DisplayControllerHandler
*/
public class BrightnessClamperController {
private static final String TAG = "BrightnessClamperController";
+ // To enable these logs, run:
+ // 'adb shell setprop persist.log.tag.BrightnessClamperController DEBUG && adb reboot'
+ private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
+ public static final float INVALID_LUX = -1f;
private final DeviceConfigParameterProvider mDeviceConfigParameterProvider;
private final Handler mHandler;
+ private final SensorManager mSensorManager;
private final ClamperChangeListener mClamperChangeListenerExternal;
private final Executor mExecutor;
private final List<BrightnessClamper<? super DisplayDeviceData>> mClampers;
@@ -66,24 +85,55 @@
private float mCustomAnimationRate = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
@Nullable
private Type mClamperType = null;
- private int mAutoBrightnessState = -1;
+ private final SensorEventListener mLightSensorListener;
+ private Sensor mRegisteredLightSensor = null;
+ private Sensor mLightSensor;
+ private String mLightSensorType;
+ private String mLightSensorName;
+ private AmbientFilter mAmbientFilter;
+ private final DisplayDeviceConfig mDisplayDeviceConfig;
+ private final Resources mResources;
+ private final int mLightSensorRate;
+ private final Injector mInjector;
private boolean mClamperApplied = false;
public BrightnessClamperController(Handler handler,
ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context,
- DisplayManagerFlags flags) {
- this(new Injector(), handler, clamperChangeListener, data, context, flags);
+ DisplayManagerFlags flags, SensorManager sensorManager) {
+ this(null, handler, clamperChangeListener, data, context, flags, sensorManager);
}
@VisibleForTesting
BrightnessClamperController(Injector injector, Handler handler,
ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context,
- DisplayManagerFlags flags) {
- mDeviceConfigParameterProvider = injector.getDeviceConfigParameterProvider();
+ DisplayManagerFlags flags, SensorManager sensorManager) {
+ mInjector = injector == null ? new Injector() : injector;
+ mDeviceConfigParameterProvider = mInjector.getDeviceConfigParameterProvider();
mHandler = handler;
+ mSensorManager = sensorManager;
+ mDisplayDeviceConfig = data.mDisplayDeviceConfig;
+ mLightSensorListener = new SensorEventListener() {
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ long now = SystemClock.elapsedRealtime();
+ mAmbientFilter.addValue(TimeUnit.NANOSECONDS.toMillis(event.timestamp),
+ event.values[0]);
+ final float lux = mAmbientFilter.getEstimate(now);
+ mModifiers.forEach(mModifier -> mModifier.setAmbientLux(lux));
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // unused
+ }
+ };
+
mClamperChangeListenerExternal = clamperChangeListener;
mExecutor = new HandlerExecutor(handler);
+ mResources = context.getResources();
+ mLightSensorRate = context.getResources().getInteger(
+ R.integer.config_autoBrightnessLightSensorRate);
Runnable clamperChangeRunnableInternal = this::recalculateBrightnessCap;
@@ -93,10 +143,10 @@
}
};
- mClampers = injector.getClampers(handler, clamperChangeListenerInternal, data, flags,
+ mClampers = mInjector.getClampers(handler, clamperChangeListenerInternal, data, flags,
context);
- mModifiers = injector.getModifiers(flags, context, handler, clamperChangeListener,
- data.mDisplayDeviceConfig);
+ mModifiers = mInjector.getModifiers(flags, context, handler, clamperChangeListener,
+ data.mDisplayDeviceConfig, mSensorManager);
mOnPropertiesChangedListener =
properties -> mClampers.forEach(BrightnessClamper::onDeviceConfigChanged);
start();
@@ -114,7 +164,7 @@
* Called in DisplayControllerHandler
*/
public DisplayBrightnessState clamp(DisplayManagerInternal.DisplayPowerRequest request,
- float brightnessValue, boolean slowChange) {
+ float brightnessValue, boolean slowChange, int displayState) {
float cappedBrightness = Math.min(brightnessValue, mBrightnessCap);
DisplayBrightnessState.Builder builder = DisplayBrightnessState.builder();
@@ -133,6 +183,12 @@
mClamperApplied = false;
}
+ if (displayState != STATE_ON) {
+ unregisterSensorListener();
+ } else {
+ maybeRegisterLightSensor();
+ }
+
for (int i = 0; i < mModifiers.size(); i++) {
mModifiers.get(i).apply(request, builder);
}
@@ -175,6 +231,8 @@
writer.println(" mBrightnessCap: " + mBrightnessCap);
writer.println(" mClamperType: " + mClamperType);
writer.println(" mClamperApplied: " + mClamperApplied);
+ writer.println(" mLightSensor=" + mLightSensor);
+ writer.println(" mRegisteredLightSensor=" + mRegisteredLightSensor);
IndentingPrintWriter ipw = new IndentingPrintWriter(writer, " ");
mClampers.forEach(clamper -> clamper.dump(ipw));
mModifiers.forEach(modifier -> modifier.dump(ipw));
@@ -191,26 +249,6 @@
mModifiers.forEach(BrightnessStateModifier::stop);
}
- /**
- * Notifies modifiers that ambient lux has changed.
- * @param ambientLux current lux, debounced
- */
- public void onAmbientLuxChange(float ambientLux) {
- mModifiers.forEach(modifier -> modifier.onAmbientLuxChange(ambientLux));
- }
-
- /**
- * Sets the autobrightness state for clampers that need to be aware of the state.
- * @param state autobrightness state
- */
- public void setAutoBrightnessState(int state) {
- if (state == mAutoBrightnessState) {
- return;
- }
- mModifiers.forEach(modifier -> modifier.setAutoBrightnessState(state));
- mAutoBrightnessState = state;
- recalculateBrightnessCap();
- }
// Called in DisplayControllerHandler
private void recalculateBrightnessCap() {
@@ -243,6 +281,10 @@
if (!mClampers.isEmpty()) {
mDeviceConfigParameterProvider.addOnPropertiesChangedListener(
mExecutor, mOnPropertiesChangedListener);
+ reloadLightSensorData(mDisplayDeviceConfig);
+ mLightSensor = mInjector.getLightSensor(
+ mSensorManager, mLightSensorType, mLightSensorName);
+ maybeRegisterLightSensor();
}
}
@@ -281,7 +323,7 @@
List<BrightnessStateModifier> getModifiers(DisplayManagerFlags flags, Context context,
Handler handler, ClamperChangeListener listener,
- DisplayDeviceConfig displayDeviceConfig) {
+ DisplayDeviceConfig displayDeviceConfig, SensorManager sensorManager) {
List<BrightnessStateModifier> modifiers = new ArrayList<>();
modifiers.add(new DisplayDimModifier(context));
modifiers.add(new BrightnessLowPowerModeModifier());
@@ -292,13 +334,19 @@
}
return modifiers;
}
+
+ Sensor getLightSensor(SensorManager sensorManager, String type, String name) {
+ return SensorUtils.findSensor(sensorManager, type,
+ name, Sensor.TYPE_LIGHT);
+ }
+
}
/**
* Config Data for clampers
*/
public static class DisplayDeviceData implements BrightnessThermalClamper.ThermalData,
- BrightnessPowerClamper.PowerData,
+ BrightnessPowerClamper.PowerData,
BrightnessWearBedtimeModeClamper.WearBedtimeModeData {
@NonNull
private final String mUniqueDisplayId;
@@ -368,4 +416,51 @@
return mDisplayDeviceConfig.getTempSensor();
}
}
+
+ private void maybeRegisterLightSensor() {
+ if (mModifiers.stream().noneMatch(BrightnessStateModifier::shouldListenToLightSensor)) {
+ return;
+ }
+
+ if (mRegisteredLightSensor == mLightSensor) {
+ return;
+ }
+
+ if (mRegisteredLightSensor != null) {
+ unregisterSensorListener();
+ }
+
+ mAmbientFilter = AmbientFilterFactory.createBrightnessFilter(TAG, mResources);
+ mSensorManager.registerListener(mLightSensorListener,
+ mLightSensor, mLightSensorRate * 1000, mHandler);
+ mRegisteredLightSensor = mLightSensor;
+
+ if (DEBUG) {
+ Slog.d(TAG, "maybeRegisterLightSensor");
+ }
+ }
+
+ private void unregisterSensorListener() {
+ mSensorManager.unregisterListener(mLightSensorListener);
+ mRegisteredLightSensor = null;
+ mModifiers.forEach(mModifier -> mModifier.setAmbientLux(INVALID_LUX)); // set lux to invalid
+ if (DEBUG) {
+ Slog.d(TAG, "unregisterSensorListener");
+ }
+ }
+
+ private void reloadLightSensorData(DisplayDeviceConfig displayDeviceConfig) {
+ // The displayDeviceConfig (ddc) contains display specific preferences. When loaded,
+ // it naturally falls back to the global config.xml.
+ if (displayDeviceConfig != null
+ && displayDeviceConfig.getAmbientLightSensor() != null) {
+ // This covers both the ddc and the config.xml fallback
+ mLightSensorType = displayDeviceConfig.getAmbientLightSensor().type;
+ mLightSensorName = displayDeviceConfig.getAmbientLightSensor().name;
+ } else if (mLightSensorName == null && mLightSensorType == null) {
+ mLightSensorType = mResources.getString(
+ com.android.internal.R.string.config_displayLightSensorType);
+ mLightSensorName = "";
+ }
+ }
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowLuxModifier.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowLuxModifier.java
index 7ba4a4d..951980a 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowLuxModifier.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowLuxModifier.java
@@ -16,7 +16,6 @@
package com.android.server.display.brightness.clamper;
-import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED;
import android.content.ContentResolver;
import android.content.Context;
@@ -30,6 +29,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.display.BrightnessSynchronizer;
+import com.android.server.display.BrightnessMappingStrategy;
import com.android.server.display.DisplayBrightnessState;
import com.android.server.display.DisplayDeviceConfig;
import com.android.server.display.brightness.BrightnessReason;
@@ -56,7 +56,6 @@
private float mBrightnessLowerBound;
private float mMinNitsAllowed;
private boolean mIsActive;
- private boolean mAutoBrightnessEnabled;
private float mAmbientLux;
private final DisplayDeviceConfig mDisplayDeviceConfig;
@@ -87,15 +86,15 @@
mContentResolver, Settings.Secure.EVEN_DIMMER_MIN_NITS,
/* def= */ MIN_NITS_DEFAULT, userId);
- boolean isActive = isSettingEnabled() && mAutoBrightnessEnabled;
-
- float luxBasedNitsLowerBound = mDisplayDeviceConfig.getMinNitsFromLux(mAmbientLux);
+ boolean isActive = isSettingEnabled()
+ && mAmbientLux != BrightnessMappingStrategy.INVALID_LUX;
final int reason;
float minNitsAllowed = -1f; // undefined, if setting is off.
final float minBrightnessAllowed;
if (isActive) {
+ float luxBasedNitsLowerBound = mDisplayDeviceConfig.getMinNitsFromLux(mAmbientLux);
minNitsAllowed = Math.max(settingNitsLowerBound,
luxBasedNitsLowerBound);
minBrightnessAllowed = getBrightnessFromNits(minNitsAllowed);
@@ -127,6 +126,12 @@
}
@VisibleForTesting
+ public void setAmbientLux(float lux) {
+ mAmbientLux = lux;
+ recalculateLowerBound();
+ }
+
+ @VisibleForTesting
public boolean isActive() {
return mIsActive;
}
@@ -164,10 +169,10 @@
@Override
public void apply(DisplayManagerInternal.DisplayPowerRequest request,
DisplayBrightnessState.Builder stateBuilder) {
+
stateBuilder.setMinBrightness(mBrightnessLowerBound);
float boundedBrightness = Math.max(mBrightnessLowerBound, stateBuilder.getBrightness());
stateBuilder.setBrightness(boundedBrightness);
-
if (BrightnessSynchronizer.floatEquals(stateBuilder.getBrightness(),
mBrightnessLowerBound)) {
stateBuilder.getBrightnessReason().addModifier(mReason);
@@ -180,14 +185,8 @@
}
@Override
- public void onAmbientLuxChange(float ambientLux) {
- mAmbientLux = ambientLux;
- recalculateLowerBound();
- }
-
- @Override
- public void setAutoBrightnessState(int state) {
- mAutoBrightnessEnabled = state == AUTO_BRIGHTNESS_ENABLED;
+ public boolean shouldListenToLightSensor() {
+ return isSettingEnabled();
}
@Override
@@ -217,6 +216,7 @@
}
private final class SettingsObserver extends ContentObserver {
+
SettingsObserver(Handler handler) {
super(handler);
mContentResolver.registerContentObserver(
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifier.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifier.java
index b478952..5661ede 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifier.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifier.java
@@ -51,4 +51,14 @@
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
super.dump(ipw);
}
+
+ @Override
+ public boolean shouldListenToLightSensor() {
+ return false;
+ }
+
+ @Override
+ public void setAmbientLux(float lux) {
+ // unused
+ }
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java
index db5a524d..be8fa5a 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java
@@ -68,14 +68,4 @@
public void stop() {
// do nothing
}
-
- @Override
- public void onAmbientLuxChange(float ambientLux) {
- // do nothing
- }
-
- @Override
- public void setAutoBrightnessState(int state) {
- // do nothing
- }
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessStateModifier.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessStateModifier.java
index 1606159c..fd40ce3 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessStateModifier.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessStateModifier.java
@@ -44,14 +44,15 @@
void stop();
/**
- * Allows modifiers to react to ambient lux changes.
- * @param ambientLux current debounced lux.
+ *
+ * @return whether the brightness state modifier needs to listen to the ambient lux in order to
+ * calculate its bounds.
*/
- void onAmbientLuxChange(float ambientLux);
+ boolean shouldListenToLightSensor();
/**
- * Sets the autobrightness state for clampers that need to be aware of the state.
- * @param state autobrightness state
+ * Current ambient lux
+ * @param lux - ambient lux
*/
- void setAutoBrightnessState(int state);
+ void setAmbientLux(float lux);
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/DisplayDimModifier.java b/services/core/java/com/android/server/display/brightness/clamper/DisplayDimModifier.java
index 4ff7bdb..ab880bf 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/DisplayDimModifier.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/DisplayDimModifier.java
@@ -76,4 +76,14 @@
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
super.dump(ipw);
}
+
+ @Override
+ public boolean shouldListenToLightSensor() {
+ return false;
+ }
+
+ @Override
+ public void setAmbientLux(float lux) {
+ // unused
+ }
}
diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
index 2b5241f..b43b35b 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
@@ -102,6 +102,9 @@
private DisplayManagerFlags mDisplayManagerFlags;
+ // Indicates if the current auto-brightness should be ramped up or down slowly.
+ private boolean mIsSlowChange;
+
@VisibleForTesting
AutomaticBrightnessStrategy(Context context, int displayId, Injector injector,
DisplayManagerFlags displayManagerFlags) {
@@ -172,6 +175,11 @@
isValid = true;
}
}
+
+ // A change is slow when the auto-brightness was already applied, and there are no new
+ // auto-brightness adjustments from an external client(e.g. Moving the slider). As such,
+ // it is important to record this value before applying the current auto-brightness.
+ mIsSlowChange = hasAppliedAutoBrightness() && !getAutoBrightnessAdjustmentChanged();
setAutoBrightnessApplied(isValid);
return isValid;
}
@@ -284,8 +292,7 @@
.setSdrBrightness(brightness)
.setBrightnessReason(brightnessReason)
.setDisplayBrightnessStrategyName(getName())
- .setIsSlowChange(hasAppliedAutoBrightness()
- && !getAutoBrightnessAdjustmentChanged())
+ .setIsSlowChange(mIsSlowChange)
.setBrightnessEvent(brightnessEvent)
.setBrightnessAdjustmentFlag(mAutoBrightnessAdjustmentReasonsFlags)
.setShouldUpdateScreenBrightnessSetting(
diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
old mode 100755
new mode 100644
diff --git a/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java b/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java
index 8a3a56c..fd3a92e 100644
--- a/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java
+++ b/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java
@@ -212,6 +212,16 @@
}
}
+ public void setChargingPolicy(int policy) throws RemoteException {
+ IHealth service = mLastService.get();
+ if (service == null) return;
+ try {
+ service.setChargingPolicy(policy);
+ } catch (UnsupportedOperationException | ServiceSpecificException ex) {
+ return;
+ }
+ }
+
private static void traceBegin(String name) {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, name);
}
diff --git a/services/core/java/com/android/server/health/OWNERS b/services/core/java/com/android/server/health/OWNERS
index 81522fc..44ab7f7 100644
--- a/services/core/java/com/android/server/health/OWNERS
+++ b/services/core/java/com/android/server/health/OWNERS
@@ -1 +1 @@
-file:platform/hardware/interfaces:/health/aidl/OWNERS
+file:platform/hardware/interfaces:/health/OWNERS
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 0bd40d1..e5dbce9 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -3352,6 +3352,10 @@
mPointerIconCache.setPointerFillStyle(fillStyle);
}
+ void setPointerScale(float scale) {
+ mPointerIconCache.setPointerScale(scale);
+ }
+
interface KeyboardBacklightControllerInterface {
default void incrementKeyboardBacklight(int deviceId) {}
default void decrementKeyboardBacklight(int deviceId) {}
diff --git a/services/core/java/com/android/server/input/InputSettingsObserver.java b/services/core/java/com/android/server/input/InputSettingsObserver.java
index 9585b49..593b091 100644
--- a/services/core/java/com/android/server/input/InputSettingsObserver.java
+++ b/services/core/java/com/android/server/input/InputSettingsObserver.java
@@ -16,6 +16,7 @@
package com.android.server.input;
+import static android.view.PointerIcon.DEFAULT_POINTER_SCALE;
import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_BLACK;
import static android.view.flags.Flags.enableVectorCursorA11ySettings;
@@ -101,7 +102,9 @@
Map.entry(Settings.Secure.getUriFor(Settings.Secure.STYLUS_POINTER_ICON_ENABLED),
(reason) -> updateStylusPointerIconEnabled()),
Map.entry(Settings.System.getUriFor(Settings.System.POINTER_FILL_STYLE),
- (reason) -> updatePointerFillStyleFromSettings()));
+ (reason) -> updatePointerFillStyleFromSettings()),
+ Map.entry(Settings.System.getUriFor(Settings.System.POINTER_SCALE),
+ (reason) -> updatePointerScaleFromSettings()));
}
/**
@@ -277,4 +280,14 @@
UserHandle.USER_CURRENT);
mService.setPointerFillStyle(pointerFillStyle);
}
+
+ private void updatePointerScaleFromSettings() {
+ if (!enableVectorCursorA11ySettings()) {
+ return;
+ }
+ final float pointerScale = Settings.System.getFloatForUser(mContext.getContentResolver(),
+ Settings.System.POINTER_SCALE, DEFAULT_POINTER_SCALE,
+ UserHandle.USER_CURRENT);
+ mService.setPointerScale(pointerScale);
+ }
}
diff --git a/services/core/java/com/android/server/input/PointerIconCache.java b/services/core/java/com/android/server/input/PointerIconCache.java
index 936e17f..44622d8 100644
--- a/services/core/java/com/android/server/input/PointerIconCache.java
+++ b/services/core/java/com/android/server/input/PointerIconCache.java
@@ -16,6 +16,7 @@
package com.android.server.input;
+import static android.view.PointerIcon.DEFAULT_POINTER_SCALE;
import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_BLACK;
import android.annotation.NonNull;
@@ -63,6 +64,8 @@
@GuardedBy("mLoadedPointerIconsByDisplayAndType")
private @PointerIcon.PointerIconVectorStyleFill int mPointerIconFillStyle =
POINTER_ICON_VECTOR_STYLE_FILL_BLACK;
+ @GuardedBy("mLoadedPointerIconsByDisplayAndType")
+ private float mPointerIconScale = DEFAULT_POINTER_SCALE;
private final DisplayManager.DisplayListener mDisplayListener =
new DisplayManager.DisplayListener() {
@@ -117,6 +120,11 @@
mUiThreadHandler.post(() -> handleSetPointerFillStyle(fillStyle));
}
+ /** Set the scale for vector pointer icons. */
+ public void setPointerScale(float scale) {
+ mUiThreadHandler.post(() -> handleSetPointerScale(scale));
+ }
+
/**
* Get a loaded system pointer icon. This will fetch the icon from the cache, or load it if
* it isn't already cached.
@@ -137,7 +145,7 @@
theme.applyStyle(PointerIcon.vectorFillStyleToResource(mPointerIconFillStyle),
/* force= */ true);
icon = PointerIcon.getLoadedSystemIcon(new ContextThemeWrapper(context, theme),
- type, mUseLargePointerIcons);
+ type, mUseLargePointerIcons, mPointerIconScale);
iconsByType.put(type, icon);
}
return Objects.requireNonNull(icon);
@@ -215,6 +223,19 @@
mNative.reloadPointerIcons();
}
+ @android.annotation.UiThread
+ private void handleSetPointerScale(float scale) {
+ synchronized (mLoadedPointerIconsByDisplayAndType) {
+ if (mPointerIconScale == scale) {
+ return;
+ }
+ mPointerIconScale = scale;
+ // Clear all cached icons on all displays.
+ mLoadedPointerIconsByDisplayAndType.clear();
+ }
+ mNative.reloadPointerIcons();
+ }
+
// Updates the cached display density for the given displayId, and returns true if
// the cached density changed.
@GuardedBy("mLoadedPointerIconsByDisplayAndType")
diff --git a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
index 2e44b6d..7d48527 100644
--- a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
+++ b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
@@ -32,6 +32,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.os.IBinder;
import android.os.ResultReceiver;
import android.util.EventLog;
@@ -137,15 +138,17 @@
@GuardedBy("ImfLock.class")
@Override
public void applyImeVisibility(IBinder windowToken, @NonNull ImeTracker.Token statsToken,
- @ImeVisibilityStateComputer.VisibilityState int state) {
+ @ImeVisibilityStateComputer.VisibilityState int state, @UserIdInt int userId) {
applyImeVisibility(windowToken, statsToken, state,
- SoftInputShowHideReason.NOT_SET /* ignore reason */);
+ SoftInputShowHideReason.NOT_SET /* ignore reason */, userId);
}
@GuardedBy("ImfLock.class")
void applyImeVisibility(IBinder windowToken, @Nullable ImeTracker.Token statsToken,
@ImeVisibilityStateComputer.VisibilityState int state,
- @SoftInputShowHideReason int reason) {
+ @SoftInputShowHideReason int reason, @UserIdInt int userId) {
+ final var bindingController = mService.getInputMethodBindingController(userId);
+ final int displayIdToShowIme = bindingController.getDisplayIdToShowIme();
switch (state) {
case STATE_SHOW_IME:
if (!Flags.refactorInsetsController()) {
@@ -165,8 +168,7 @@
// NOT_FOCUSABLE, ALT_FOCUSABLE_IM flags set and can the IME target.
// Send it to window manager to hide IME from the actual IME control target
// of the target display.
- mWindowManagerInternal.hideIme(windowToken,
- mService.getDisplayIdToShowImeLocked(), statsToken);
+ mWindowManagerInternal.hideIme(windowToken, displayIdToShowIme, statsToken);
} else {
ImeTracker.forLogging().onFailed(statsToken,
ImeTracker.PHASE_SERVER_APPLY_IME_VISIBILITY);
@@ -201,10 +203,10 @@
}
break;
case STATE_SHOW_IME_SNAPSHOT:
- showImeScreenshot(windowToken, mService.getDisplayIdToShowImeLocked());
+ showImeScreenshot(windowToken, displayIdToShowIme);
break;
case STATE_REMOVE_IME_SNAPSHOT:
- removeImeScreenshot(mService.getDisplayIdToShowImeLocked());
+ removeImeScreenshot(displayIdToShowIme);
break;
default:
throw new IllegalArgumentException("Invalid IME visibility state: " + state);
diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java
index 9f2b84d..a5f9b7a 100644
--- a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java
+++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java
@@ -17,6 +17,7 @@
package com.android.server.inputmethod;
import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.os.IBinder;
import android.os.ResultReceiver;
import android.view.inputmethod.ImeTracker;
@@ -63,7 +64,7 @@
* @param state The new IME visibility state for the applier to handle
*/
default void applyImeVisibility(IBinder windowToken, @NonNull ImeTracker.Token statsToken,
- @ImeVisibilityStateComputer.VisibilityState int state) {}
+ @ImeVisibilityStateComputer.VisibilityState int state, @UserIdInt int userId) {}
/**
* Updates the IME Z-ordering relative to the given window.
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index 8191ee1..69452310 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -17,7 +17,9 @@
package com.android.server.inputmethod;
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
+import static android.content.Context.DEVICE_ID_DEFAULT;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.view.Display.INVALID_DISPLAY;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -82,12 +84,16 @@
@GuardedBy("ImfLock.class") @Nullable private IInputMethodInvoker mCurMethod;
@GuardedBy("ImfLock.class") private int mCurMethodUid = Process.INVALID_UID;
@GuardedBy("ImfLock.class") @Nullable private IBinder mCurToken;
- @GuardedBy("ImfLock.class") private int mCurTokenDisplayId = Display.INVALID_DISPLAY;
+ @GuardedBy("ImfLock.class") private int mCurTokenDisplayId = INVALID_DISPLAY;
@GuardedBy("ImfLock.class") private int mCurSeq;
@GuardedBy("ImfLock.class") private boolean mVisibleBound;
@GuardedBy("ImfLock.class") private boolean mSupportsStylusHw;
@GuardedBy("ImfLock.class") private boolean mSupportsConnectionlessStylusHw;
+ /** The display id for which the latest startInput was called. */
+ @GuardedBy("ImfLock.class") private int mDisplayIdToShowIme = INVALID_DISPLAY;
+ @GuardedBy("ImfLock.class") private int mDeviceIdToShowIme = DEVICE_ID_DEFAULT;
+
@Nullable private CountDownLatch mLatchForTesting;
/**
@@ -189,6 +195,18 @@
}
/**
+ * Returns {@link InputMethodInfo} that is queried from {@link #getSelectedMethodId()}.
+ *
+ * @return {@link InputMethodInfo} whose IME ID is the same as {@link #getSelectedMethodId()}.
+ * {@code null} otherwise
+ */
+ @GuardedBy("ImfLock.class")
+ @Nullable
+ InputMethodInfo getSelectedMethod() {
+ return InputMethodSettingsRepository.get(mUserId).getMethodMap().get(mSelectedMethodId);
+ }
+
+ /**
* The token we have made for the currently active input method, to
* identify it in the future.
*/
@@ -455,7 +473,7 @@
mWindowManagerInternal.removeWindowToken(mCurToken, true /* removeWindows */,
false /* animateExit */, mCurTokenDisplayId);
mCurToken = null;
- mCurTokenDisplayId = Display.INVALID_DISPLAY;
+ mCurTokenDisplayId = INVALID_DISPLAY;
}
@GuardedBy("ImfLock.class")
@@ -478,16 +496,15 @@
mCurId = info.getId();
mLastBindTime = SystemClock.uptimeMillis();
- final int displayIdToShowIme = mService.getDisplayIdToShowImeLocked();
mCurToken = new Binder();
- mCurTokenDisplayId = displayIdToShowIme;
+ mCurTokenDisplayId = mDisplayIdToShowIme;
if (DEBUG) {
Slog.v(TAG, "Adding window token: " + mCurToken + " for display: "
- + displayIdToShowIme);
+ + mDisplayIdToShowIme);
}
mWindowManagerInternal.addWindowToken(mCurToken,
WindowManager.LayoutParams.TYPE_INPUT_METHOD,
- displayIdToShowIme, null /* options */);
+ mDisplayIdToShowIme, null /* options */);
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
null, null, null, mCurId, mCurSeq, false);
@@ -596,4 +613,24 @@
unbindVisibleConnection();
}
}
+
+ @GuardedBy("ImfLock.class")
+ void setDisplayIdToShowIme(int displayId) {
+ mDisplayIdToShowIme = displayId;
+ }
+
+ @GuardedBy("ImfLock.class")
+ int getDisplayIdToShowIme() {
+ return mDisplayIdToShowIme;
+ }
+
+ @GuardedBy("ImfLock.class")
+ void setDeviceIdToShowIme(int deviceId) {
+ mDeviceIdToShowIme = deviceId;
+ }
+
+ @GuardedBy("ImfLock.class")
+ int getDeviceIdToShowIme() {
+ return mDeviceIdToShowIme;
+ }
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 8665a39..99036c6 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -227,6 +227,16 @@
}
/**
+ * Indicates that the annotated field is shared by all the users.
+ *
+ * <p>See b/305849394 for details.</p>
+ */
+ @Retention(SOURCE)
+ @Target({ElementType.FIELD})
+ private @interface SharedByAllUsersField {
+ }
+
+ /**
* Indicates that the annotated field is not yet ready for concurrent multi-user support.
*
* <p>See b/305849394 for details.</p>
@@ -272,6 +282,7 @@
* {@link LayoutParams#SOFT_INPUT_STATE_ALWAYS_VISIBLE SOFT_INPUT_STATE_ALWAYS_VISIBLE}
* starting from {@link android.os.Build.VERSION_CODES#P}.
*/
+ @SharedByAllUsersField
private final boolean mPreventImeStartupUnlessTextEditor;
/**
@@ -279,6 +290,7 @@
* from the IME startup avoidance behavior that is enabled by
* {@link #mPreventImeStartupUnlessTextEditor}.
*/
+ @SharedByAllUsersField
@NonNull
private final String[] mNonPreemptibleInputMethods;
@@ -286,6 +298,7 @@
* See {@link #shouldEnableExperimentalConcurrentMultiUserMode(Context)} about when set to be
* {@code true}.
*/
+ @SharedByAllUsersField
private final boolean mExperimentalConcurrentMultiUserModeEnabled;
/**
@@ -327,6 +340,7 @@
final PackageManagerInternal mPackageManagerInternal;
final InputManagerInternal mInputManagerInternal;
final ImePlatformCompatUtils mImePlatformCompatUtils;
+ @SharedByAllUsersField
final InputMethodDeviceConfigs mInputMethodDeviceConfigs;
private final UserManagerInternal mUserManagerInternal;
@@ -339,6 +353,7 @@
private final ImeVisibilityStateComputer mVisibilityStateComputer;
@GuardedBy("ImfLock.class")
+ @SharedByAllUsersField
@NonNull
private final DefaultImeVisibilityApplier mVisibilityApplier;
@@ -355,7 +370,7 @@
// Mapping from deviceId to the device-specific imeId for that device.
@GuardedBy("ImfLock.class")
- @MultiUserUnawareField
+ @SharedByAllUsersField
private final SparseArray<String> mVirtualDeviceMethodMap = new SparseArray<>();
// TODO: Instantiate mSwitchingController for each user.
@@ -367,36 +382,15 @@
@MultiUserUnawareField
private HardwareKeyboardShortcutController mHardwareKeyboardShortcutController;
- /**
- * Tracks how many times {@link #mSettings} was updated.
- */
- @GuardedBy("ImfLock.class")
- private int mMethodMapUpdateCount = 0;
-
- /**
- * The display id for which the latest startInput was called.
- */
- @GuardedBy("ImfLock.class")
- int getDisplayIdToShowImeLocked() {
- return mDisplayIdToShowIme;
- }
-
- @GuardedBy("ImfLock.class")
- @MultiUserUnawareField
- private int mDisplayIdToShowIme = INVALID_DISPLAY;
-
- @GuardedBy("ImfLock.class")
- @MultiUserUnawareField
- private int mDeviceIdToShowIme = DEVICE_ID_DEFAULT;
-
@Nullable
private StatusBarManagerInternal mStatusBarManagerInternal;
+ @SharedByAllUsersField
private boolean mShowOngoingImeSwitcherForPhones;
@GuardedBy("ImfLock.class")
@MultiUserUnawareField
private final HandwritingModeController mHwController;
@GuardedBy("ImfLock.class")
- @MultiUserUnawareField
+ @SharedByAllUsersField
private IntArray mStylusIds;
@GuardedBy("ImfLock.class")
@@ -475,6 +469,7 @@
/**
* Manages the IME clients.
*/
+ @SharedByAllUsersField
private final ClientController mClientController;
/**
@@ -486,14 +481,22 @@
/**
* Set once the system is ready to run third party code.
*/
+ @SharedByAllUsersField
boolean mSystemReady;
@GuardedBy("ImfLock.class")
@NonNull
- InputMethodBindingController getInputMethodBindingController(@UserIdInt int userId) {
- return mUserDataRepository.getOrCreate(userId).mBindingController;
+ UserDataRepository.UserData getUserData(@UserIdInt int userId) {
+ return mUserDataRepository.getOrCreate(userId);
}
+ @GuardedBy("ImfLock.class")
+ @NonNull
+ InputMethodBindingController getInputMethodBindingController(@UserIdInt int userId) {
+ return getUserData(userId).mBindingController;
+ }
+
+
/**
* Id obtained with {@link InputMethodInfo#getId()} for the currently selected input method.
* This is to be synchronized with the secure settings keyed with
@@ -522,6 +525,7 @@
/**
* The client that is currently bound to an input method.
*/
+ @MultiUserUnawareField
@Nullable
private ClientState mCurClient;
@@ -573,6 +577,7 @@
* {@link android.view.InsetsController} for the given window.
*/
@GuardedBy("ImfLock.class")
+ @SharedByAllUsersField
private final WeakHashMap<IBinder, Boolean> mFocusedWindowPerceptible = new WeakHashMap<>();
/**
@@ -677,28 +682,36 @@
@MultiUserUnawareField
int mImeWindowVis;
+ @SharedByAllUsersField
private final MyPackageMonitor mMyPackageMonitor = new MyPackageMonitor();
+
+ @SharedByAllUsersField
private final String mSlotIme;
/**
* Registered {@link InputMethodListListener}.
* This variable can be accessed from both of MainThread and BinderThread.
*/
+ @SharedByAllUsersField
private final CopyOnWriteArrayList<InputMethodListListener> mInputMethodListListeners =
new CopyOnWriteArrayList<>();
@GuardedBy("ImfLock.class")
+ @SharedByAllUsersField
private final WeakHashMap<IBinder, IBinder> mImeTargetWindowMap = new WeakHashMap<>();
@GuardedBy("ImfLock.class")
+ @SharedByAllUsersField
@NonNull
private final StartInputHistory mStartInputHistory = new StartInputHistory();
@GuardedBy("ImfLock.class")
+ @SharedByAllUsersField
@NonNull
private final SoftInputShowHideHistory mSoftInputShowHideHistory =
new SoftInputShowHideHistory();
+ @SharedByAllUsersField
@NonNull
private final ImeTrackerService mImeTrackerService;
@@ -1162,7 +1175,7 @@
final int userId = user.getUserIdentifier();
SecureSettingsWrapper.onUserStarting(userId);
synchronized (ImfLock.class) {
- mService.mUserDataRepository.getOrCreate(userId);
+ mService.getUserData(userId);
if (mService.mExperimentalConcurrentMultiUserModeEnabled) {
if (mService.mCurrentUserId != userId && mService.mSystemReady) {
mService.experimentalInitializeVisibleBackgroundUserLocked(userId);
@@ -1284,7 +1297,7 @@
bindingControllerForTesting != null ? bindingControllerForTesting
: bindingControllerFactory);
for (int id : mUserManagerInternal.getUserIds()) {
- mUserDataRepository.getOrCreate(id);
+ getUserData(id);
}
final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
@@ -1951,7 +1964,7 @@
final var statsToken = createStatsTokenForFocusedClient(false /* show */,
SoftInputShowHideReason.UNBIND_CURRENT_METHOD);
mVisibilityApplier.applyImeVisibility(mImeBindingState.mFocusedWindow, statsToken,
- STATE_HIDE_IME);
+ STATE_HIDE_IME, mCurrentUserId);
}
}
@@ -1992,17 +2005,18 @@
@GuardedBy("ImfLock.class")
@NonNull
InputBindResult attachNewInputLocked(@StartInputReason int startInputReason, boolean initial) {
+ final int userId = mCurrentUserId;
+ final var bindingController = getInputMethodBindingController(userId);
if (!mBoundToMethod) {
- getCurMethodLocked().bindInput(mCurClient.mBinding);
+ bindingController.getCurMethod().bindInput(mCurClient.mBinding);
mBoundToMethod = true;
}
final boolean restarting = !initial;
final Binder startInputToken = new Binder();
- final var bindingController = getInputMethodBindingController(mCurrentUserId);
final StartInputInfo info = new StartInputInfo(mCurrentUserId,
- getCurTokenLocked(),
- getCurTokenDisplayIdLocked(), bindingController.getCurId(), startInputReason,
+ bindingController.getCurToken(), bindingController.getCurTokenDisplayId(),
+ bindingController.getCurId(), startInputReason,
restarting, UserHandle.getUserId(mCurClient.mUid),
mCurClient.mSelfReportedDisplayId, mImeBindingState.mFocusedWindow, mCurEditorInfo,
mImeBindingState.mFocusedWindowSoftInputMode,
@@ -2015,9 +2029,8 @@
// same-user scenarios.
// That said ignoring cross-user scenario will never affect IMEs that do not have
// INTERACT_ACROSS_USERS(_FULL) permissions, which is actually almost always the case.
- if (mCurrentUserId == UserHandle.getUserId(
- mCurClient.mUid)) {
- mPackageManagerInternal.grantImplicitAccess(mCurrentUserId, null /* intent */,
+ if (userId == UserHandle.getUserId(mCurClient.mUid)) {
+ mPackageManagerInternal.grantImplicitAccess(userId, null /* intent */,
UserHandle.getAppId(bindingController.getCurMethodUid()),
mCurClient.mUid, true /* direct */);
}
@@ -2047,7 +2060,7 @@
}
final var curId = bindingController.getCurId();
- final InputMethodInfo curInputMethodInfo = InputMethodSettingsRepository.get(mCurrentUserId)
+ final InputMethodInfo curInputMethodInfo = InputMethodSettingsRepository.get(userId)
.getMethodMap().get(curId);
final boolean suppressesSpellChecker =
curInputMethodInfo != null && curInputMethodInfo.suppressesSpellChecker();
@@ -2122,7 +2135,8 @@
return InputBindResult.NOT_IME_TARGET_WINDOW;
}
final int csDisplayId = cs.mSelfReportedDisplayId;
- mDisplayIdToShowIme = mVisibilityStateComputer.computeImeDisplayId(winState, csDisplayId);
+ bindingController.setDisplayIdToShowIme(
+ mVisibilityStateComputer.computeImeDisplayId(winState, csDisplayId));
// Potentially override the selected input method if the new display belongs to a virtual
// device with a custom IME.
@@ -2132,7 +2146,8 @@
if (deviceMethodId == null) {
mVisibilityStateComputer.getImePolicy().setImeHiddenByDisplayPolicy(true);
} else if (!Objects.equals(deviceMethodId, selectedMethodId)) {
- setInputMethodLocked(deviceMethodId, NOT_A_SUBTYPE_ID, mDeviceIdToShowIme);
+ setInputMethodLocked(deviceMethodId, NOT_A_SUBTYPE_ID,
+ bindingController.getDeviceIdToShowIme());
selectedMethodId = deviceMethodId;
}
@@ -2193,8 +2208,9 @@
// We expect the caller has already verified that the client is allowed to access this
// display ID.
final String curId = bindingController.getCurId();
+ final int displayIdToShowIme = bindingController.getDisplayIdToShowIme();
if (curId != null && curId.equals(bindingController.getSelectedMethodId())
- && mDisplayIdToShowIme == getCurTokenDisplayIdLocked()) {
+ && displayIdToShowIme == getCurTokenDisplayIdLocked()) {
if (cs.mCurSession != null) {
// Fast case: if we are already connected to the input method,
// then just return it.
@@ -2244,9 +2260,12 @@
}
final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
- final int oldDeviceId = mDeviceIdToShowIme;
- mDeviceIdToShowIme = mVdmInternal.getDeviceIdForDisplayId(mDisplayIdToShowIme);
- if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) {
+ final var bindingController = getInputMethodBindingController(userId);
+ final int oldDeviceId = bindingController.getDeviceIdToShowIme();
+ final int displayIdToShowIme = bindingController.getDisplayIdToShowIme();
+ final int newDeviceId = mVdmInternal.getDeviceIdForDisplayId(displayIdToShowIme);
+ bindingController.setDeviceIdToShowIme(newDeviceId);
+ if (newDeviceId == DEVICE_ID_DEFAULT) {
if (oldDeviceId == DEVICE_ID_DEFAULT) {
return currentMethodId;
}
@@ -2258,13 +2277,12 @@
return defaultDeviceMethodId;
}
- final String deviceMethodId =
- mVirtualDeviceMethodMap.get(mDeviceIdToShowIme, currentMethodId);
+ final String deviceMethodId = mVirtualDeviceMethodMap.get(newDeviceId, currentMethodId);
if (Objects.equals(deviceMethodId, currentMethodId)) {
return currentMethodId;
} else if (!settings.getMethodMap().containsKey(deviceMethodId)) {
if (DEBUG) {
- Slog.v(TAG, "Disabling IME on virtual device with id " + mDeviceIdToShowIme
+ Slog.v(TAG, "Disabling IME on virtual device with id " + newDeviceId
+ " because its custom input method is not available: " + deviceMethodId);
}
return null;
@@ -2279,7 +2297,7 @@
if (DEBUG) {
Slog.v(TAG, "Switching current input method from " + currentMethodId
+ " to device-specific one " + deviceMethodId + " because the current display "
- + mDisplayIdToShowIme + " belongs to device with id " + mDeviceIdToShowIme);
+ + displayIdToShowIme + " belongs to device with id " + newDeviceId);
}
return deviceMethodId;
}
@@ -2478,8 +2496,7 @@
@GuardedBy("ImfLock.class")
void resetCurrentMethodAndClientLocked(@UnbindReason int unbindClientReason) {
- final var bindingController =
- mUserDataRepository.getOrCreate(mCurrentUserId).mBindingController;
+ final var bindingController = getInputMethodBindingController(mCurrentUserId);
bindingController.setSelectedMethodId(null);
// Callback before clean-up binding states.
@@ -2920,7 +2937,7 @@
* <li>
* {@link PackageManager#COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED} is not updated.
* </li>
- * <li>{@link #mDeviceIdToShowIme} is ignored.</li>
+ * <li>{@link InputMethodBindingController#getDeviceIdToShowIme()} is ignored.</li>
* <li>{@link #mSwitchingController} is ignored.</li>
* <li>{@link #mHardwareKeyboardShortcutController} is ignored.</li>
* <li>{@link #mPreventImeStartupUnlessTextEditor} is ignored.</li>
@@ -2959,10 +2976,11 @@
@GuardedBy("ImfLock.class")
void updateInputMethodsFromSettingsLocked(boolean enabledMayChange) {
- final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+ final int userId = mCurrentUserId;
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
if (enabledMayChange) {
final PackageManager userAwarePackageManager = getPackageManagerForUser(mContext,
- settings.getUserId());
+ userId);
List<InputMethodInfo> enabled = settings.getEnabledInputMethodList();
for (int i = 0; i < enabled.size(); i++) {
@@ -2989,22 +3007,22 @@
}
}
- if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) {
+ final var bindingController = getInputMethodBindingController(mCurrentUserId);
+ if (bindingController.getDeviceIdToShowIme() == DEVICE_ID_DEFAULT) {
String ime = SecureSettingsWrapper.getString(
- Settings.Secure.DEFAULT_INPUT_METHOD, null, settings.getUserId());
+ Settings.Secure.DEFAULT_INPUT_METHOD, null, userId);
String defaultDeviceIme = SecureSettingsWrapper.getString(
- Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, settings.getUserId());
+ Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, userId);
if (defaultDeviceIme != null && !Objects.equals(ime, defaultDeviceIme)) {
if (DEBUG) {
Slog.v(TAG, "Current input method " + ime + " differs from the stored default"
- + " device input method for user " + settings.getUserId()
+ + " device input method for user " + userId
+ " - restoring " + defaultDeviceIme);
}
SecureSettingsWrapper.putString(
- Settings.Secure.DEFAULT_INPUT_METHOD, defaultDeviceIme,
- settings.getUserId());
+ Settings.Secure.DEFAULT_INPUT_METHOD, defaultDeviceIme, userId);
SecureSettingsWrapper.putString(
- Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, settings.getUserId());
+ Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, userId);
}
}
@@ -3030,18 +3048,18 @@
}
// TODO: Instantiate mSwitchingController for each user.
- if (settings.getUserId() == mSwitchingController.getUserId()) {
+ if (userId == mSwitchingController.getUserId()) {
mSwitchingController.resetCircularListLocked(settings.getMethodMap());
} else {
mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
- mContext, settings.getMethodMap(), settings.getUserId());
+ mContext, settings.getMethodMap(), userId);
}
// TODO: Instantiate mHardwareKeyboardShortcutController for each user.
- if (settings.getUserId() == mHardwareKeyboardShortcutController.getUserId()) {
+ if (userId == mHardwareKeyboardShortcutController.getUserId()) {
mHardwareKeyboardShortcutController.reset(settings.getMethodMap());
} else {
mHardwareKeyboardShortcutController = new HardwareKeyboardShortcutController(
- settings.getMethodMap(), settings.getUserId());
+ settings.getMethodMap(), userId);
}
sendOnNavButtonFlagsChangedLocked();
}
@@ -3065,15 +3083,16 @@
@GuardedBy("ImfLock.class")
void setInputMethodLocked(String id, int subtypeId, int deviceId) {
- final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+ final int userId = mCurrentUserId;
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
InputMethodInfo info = settings.getMethodMap().get(id);
if (info == null) {
throw getExceptionForUnknownImeId(id);
}
+ final var bindingController = getInputMethodBindingController(userId);
// See if we need to notify a subtype change within the same IME.
- if (id.equals(getSelectedMethodIdLocked())) {
- final int userId = settings.getUserId();
+ if (id.equals(bindingController.getSelectedMethodId())) {
final int subtypeCount = info.getSubtypeCount();
if (subtypeCount <= 0) {
notifyInputMethodSubtypeChangedLocked(userId, info, null);
@@ -3109,7 +3128,8 @@
}
// Changing to a different IME.
- if (mDeviceIdToShowIme != DEVICE_ID_DEFAULT && deviceId == DEVICE_ID_DEFAULT) {
+ if (bindingController.getDeviceIdToShowIme() != DEVICE_ID_DEFAULT
+ && deviceId == DEVICE_ID_DEFAULT) {
// This change should only be applicable to the default device but the current input
// method is a custom one specific to a virtual device. So only update the settings
// entry used to restore the default device input method once we want to show the IME
@@ -3129,7 +3149,7 @@
// mCurMethodId should be updated after setSelectedInputMethodAndSubtypeLocked()
// because mCurMethodId is stored as a history in
// setSelectedInputMethodAndSubtypeLocked().
- getInputMethodBindingController(mCurrentUserId).setSelectedMethodId(id);
+ bindingController.setSelectedMethodId(id);
if (mActivityManagerInternal.isSystemReady()) {
Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED);
@@ -4172,10 +4192,10 @@
@GuardedBy("ImfLock.class")
private boolean switchToNextInputMethodLocked(@Nullable IBinder token, boolean onlyCurrentIme) {
- final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+ final int userId = mCurrentUserId;
+ final var currentImi = getInputMethodBindingController(userId).getSelectedMethod();
final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
- onlyCurrentIme, settings.getMethodMap().get(getSelectedMethodIdLocked()),
- mCurrentSubtype);
+ onlyCurrentIme, currentImi, mCurrentSubtype);
if (nextSubtype == null) {
return false;
}
@@ -4190,10 +4210,10 @@
if (!calledWithValidTokenLocked(token)) {
return false;
}
- final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+ final int userId = mCurrentUserId;
+ final var currentImi = getInputMethodBindingController(userId).getSelectedMethod();
final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
- false /* onlyCurrentIme */,
- settings.getMethodMap().get(getSelectedMethodIdLocked()), mCurrentSubtype);
+ false /* onlyCurrentIme */, currentImi, mCurrentSubtype);
return nextSubtype != null;
}
}
@@ -4628,8 +4648,7 @@
if (mCurrentUserId != mSwitchingController.getUserId()) {
return;
}
- final InputMethodInfo imi = InputMethodSettingsRepository.get(mCurrentUserId)
- .getMethodMap().get(getSelectedMethodIdLocked());
+ final var imi = getInputMethodBindingController(mCurrentUserId).getSelectedMethod();
if (imi != null) {
mSwitchingController.onUserActionLocked(imi, mCurrentSubtype);
}
@@ -4653,7 +4672,7 @@
windowToken);
mVisibilityApplier.applyImeVisibility(requestToken, statsToken,
setVisible ? ImeVisibilityStateComputer.STATE_SHOW_IME
- : ImeVisibilityStateComputer.STATE_HIDE_IME);
+ : ImeVisibilityStateComputer.STATE_HIDE_IME, mCurrentUserId);
}
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -5226,9 +5245,9 @@
Slog.e(TAG, "buildInputMethodListLocked is not allowed until system is ready");
return;
}
- mMethodMapUpdateCount++;
- final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+ final int userId = mCurrentUserId;
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
boolean reenableMinimumNonAuxSystemImes = false;
// TODO: The following code should find better place to live.
@@ -5291,18 +5310,18 @@
updateDefaultVoiceImeIfNeededLocked();
// TODO: Instantiate mSwitchingController for each user.
- if (settings.getUserId() == mSwitchingController.getUserId()) {
+ if (userId == mSwitchingController.getUserId()) {
mSwitchingController.resetCircularListLocked(settings.getMethodMap());
} else {
mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
mContext, settings.getMethodMap(), mCurrentUserId);
}
// TODO: Instantiate mHardwareKeyboardShortcutController for each user.
- if (settings.getUserId() == mHardwareKeyboardShortcutController.getUserId()) {
+ if (userId == mHardwareKeyboardShortcutController.getUserId()) {
mHardwareKeyboardShortcutController.reset(settings.getMethodMap());
} else {
mHardwareKeyboardShortcutController = new HardwareKeyboardShortcutController(
- settings.getMethodMap(), settings.getUserId());
+ settings.getMethodMap(), userId);
}
sendOnNavButtonFlagsChangedLocked();
@@ -5310,7 +5329,7 @@
// Notify InputMethodListListeners of the new installed InputMethods.
final List<InputMethodInfo> inputMethodList = settings.getMethodList();
mHandler.obtainMessage(MSG_DISPATCH_ON_INPUT_METHOD_LIST_UPDATED,
- settings.getUserId(), 0 /* unused */, inputMethodList).sendToTarget();
+ userId, 0 /* unused */, inputMethodList).sendToTarget();
}
@GuardedBy("ImfLock.class")
@@ -5368,7 +5387,8 @@
*/
@GuardedBy("ImfLock.class")
private boolean setInputMethodEnabledLocked(String id, boolean enabled) {
- final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+ final int userId = mCurrentUserId;
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
if (enabled) {
final String enabledImeIdsStr = settings.getEnabledInputMethodsStr();
final String newEnabledImeIdsStr = InputMethodUtils.concatEnabledImeIds(
@@ -5387,7 +5407,8 @@
StringBuilder builder = new StringBuilder();
if (settings.buildAndPutEnabledInputMethodsStrRemovingId(
builder, enabledInputMethodsList, id)) {
- if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) {
+ final var bindingController = getInputMethodBindingController(userId);
+ if (bindingController.getDeviceIdToShowIme() == DEVICE_ID_DEFAULT) {
// Disabled input method is currently selected, switch to another one.
final String selId = settings.getSelectedInputMethod();
if (id.equals(selId) && !chooseNewDefaultIMELocked()) {
@@ -5445,8 +5466,9 @@
@GuardedBy("ImfLock.class")
private void resetSelectedInputMethodAndSubtypeLocked(String newDefaultIme) {
- mDeviceIdToShowIme = DEVICE_ID_DEFAULT;
- mDisplayIdToShowIme = INVALID_DISPLAY;
+ final var bindingController = getInputMethodBindingController(mCurrentUserId);
+ bindingController.setDisplayIdToShowIme(INVALID_DISPLAY);
+ bindingController.setDeviceIdToShowIme(DEVICE_ID_DEFAULT);
final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
settings.putSelectedDefaultDeviceInputMethod(null);
@@ -5503,44 +5525,18 @@
*/
@GuardedBy("ImfLock.class")
InputMethodSubtype getCurrentInputMethodSubtypeLocked() {
- String selectedMethodId = getSelectedMethodIdLocked();
+ final int userId = mCurrentUserId;
+ final var selectedMethodId = getInputMethodBindingController(userId).getSelectedMethodId();
if (selectedMethodId == null) {
return null;
}
- final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
- final boolean subtypeIsSelected = settings.isSubtypeSelected();
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
final InputMethodInfo imi = settings.getMethodMap().get(selectedMethodId);
if (imi == null || imi.getSubtypeCount() == 0) {
return null;
}
- if (!subtypeIsSelected || mCurrentSubtype == null
- || !SubtypeUtils.isValidSubtypeId(imi, mCurrentSubtype.hashCode())) {
- int subtypeId = settings.getSelectedInputMethodSubtypeId(selectedMethodId);
- if (subtypeId == NOT_A_SUBTYPE_ID) {
- // If there are no selected subtypes, the framework will try to find
- // the most applicable subtype from explicitly or implicitly enabled
- // subtypes.
- List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypes =
- settings.getEnabledInputMethodSubtypeList(imi, true);
- // If there is only one explicitly or implicitly enabled subtype,
- // just returns it.
- if (explicitlyOrImplicitlyEnabledSubtypes.size() == 1) {
- mCurrentSubtype = explicitlyOrImplicitlyEnabledSubtypes.get(0);
- } else if (explicitlyOrImplicitlyEnabledSubtypes.size() > 1) {
- final String locale = SystemLocaleWrapper.get(settings.getUserId())
- .get(0).toString();
- mCurrentSubtype = SubtypeUtils.findLastResortApplicableSubtype(
- explicitlyOrImplicitlyEnabledSubtypes,
- SubtypeUtils.SUBTYPE_MODE_KEYBOARD, locale, true);
- if (mCurrentSubtype == null) {
- mCurrentSubtype = SubtypeUtils.findLastResortApplicableSubtype(
- explicitlyOrImplicitlyEnabledSubtypes, null, locale, true);
- }
- }
- } else {
- mCurrentSubtype = SubtypeUtils.getSubtypes(imi).get(subtypeId);
- }
- }
+ mCurrentSubtype = SubtypeUtils.getCurrentInputMethodSubtype(imi, settings,
+ mCurrentSubtype);
return mCurrentSubtype;
}
@@ -5909,27 +5905,36 @@
synchronized (ImfLock.class) {
final int uid = Binder.getCallingUid();
- if (getSelectedMethodIdLocked() == null) {
+ final int imeUserId = UserHandle.getUserId(uid);
+ if (imeUserId != mCurrentUserId) {
+ // Currently concurrent multi-user is not supported here due to the remaining
+ // dependency on mCurEditorInfo and mCurClient.
+ // TODO(b/341558132): Remove this early-exit once it becomes multi-user ready.
+ Slog.i(TAG, "Ignoring createInputContentUriToken due to user ID mismatch."
+ + " imeUserId=" + imeUserId + " mCurrentUserId=" + mCurrentUserId);
return null;
}
- if (getCurTokenLocked() != token) {
- Slog.e(TAG, "Ignoring createInputContentUriToken mCurToken=" + getCurTokenLocked()
- + " token=" + token);
+ final var bindingController = getInputMethodBindingController(imeUserId);
+ if (bindingController.getSelectedMethodId() == null) {
+ return null;
+ }
+ if (bindingController.getCurToken() != token) {
+ Slog.e(TAG, "Ignoring createInputContentUriToken mCurToken="
+ + bindingController.getCurToken() + " token=" + token);
return null;
}
// We cannot simply distinguish a bad IME that reports an arbitrary package name from
// an unfortunate IME whose internal state is already obsolete due to the asynchronous
// nature of our system. Let's compare it with our internal record.
- final var curPackageName = mCurEditorInfo != null
- ? mCurEditorInfo.packageName : null;
+ // TODO(b/341558132): Use "imeUserId" to query per-user "curEditorInfo"
+ final var curPackageName = mCurEditorInfo != null ? mCurEditorInfo.packageName : null;
if (!TextUtils.equals(curPackageName, packageName)) {
Slog.e(TAG, "Ignoring createInputContentUriToken mCurEditorInfo.packageName="
+ curPackageName + " packageName=" + packageName);
return null;
}
- // This user ID can never bee spoofed.
- final int imeUserId = UserHandle.getUserId(uid);
- // This user ID can never bee spoofed.
+ // This user ID can never be spoofed.
+ // TODO(b/341558132): Use "imeUserId" to query per-user "curClient"
final int appUserId = UserHandle.getUserId(mCurClient.mUid);
// This user ID may be invalid if "contentUri" embedded an invalid user ID.
final int contentUriOwnerUserId = ContentProvider.getUserIdFromUri(contentUri,
@@ -6051,7 +6056,7 @@
p.println("Current Input Method Manager state:");
final List<InputMethodInfo> methodList = settings.getMethodList();
int numImes = methodList.size();
- p.println(" Input Methods: mMethodMapUpdateCount=" + mMethodMapUpdateCount);
+ p.println(" Input Methods:");
for (int i = 0; i < numImes; i++) {
InputMethodInfo info = methodList.get(i);
p.println(" InputMethod #" + i + ":");
@@ -6118,7 +6123,7 @@
p.println(" mStylusIds=" + (mStylusIds != null
? Arrays.toString(mStylusIds.toArray()) : ""));
p.println(" mSwitchingController:");
- mSwitchingController.dump(p);
+ mSwitchingController.dump(p, " ");
p.println(" mStartInputHistory:");
mStartInputHistory.dump(pw, " ");
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSettings.java b/services/core/java/com/android/server/inputmethod/InputMethodSettings.java
index a558838..7ce4074 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodSettings.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSettings.java
@@ -51,8 +51,23 @@
public static final boolean DEBUG = false;
private static final String TAG = "InputMethodSettings";
- private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;
- private static final String NOT_A_SUBTYPE_ID_STR = String.valueOf(NOT_A_SUBTYPE_ID);
+ /**
+ * An integer code that represents "no subtype" when a subtype hashcode is used.
+ *
+ * <p>Due to historical confusions with {@link InputMethodUtils#NOT_A_SUBTYPE_ID}, we have
+ * used {@code -1} here. We cannot change this value as it's already saved into secure settings.
+ * </p>
+ */
+ private static final int INVALID_SUBTYPE_HASHCODE = -1;
+ /**
+ * A string code that represents "no subtype" when a subtype hashcode is used.
+ *
+ * <p>Due to historical confusions with {@link InputMethodUtils#NOT_A_SUBTYPE_ID}, we have
+ * used {@code "-1"} here. We cannot change this value as it's already saved into secure
+ * settings.</p>
+ */
+ private static final String INVALID_SUBTYPE_HASHCODE_STR =
+ String.valueOf(INVALID_SUBTYPE_HASHCODE);
private static final char INPUT_METHOD_SEPARATOR = InputMethodUtils.INPUT_METHOD_SEPARATOR;
private static final char INPUT_METHOD_SUBTYPE_SEPARATOR =
InputMethodUtils.INPUT_METHOD_SUBTYPE_SEPARATOR;
@@ -259,34 +274,33 @@
}
private void saveSubtypeHistory(
- List<Pair<String, String>> savedImes, String newImeId, String newSubtypeId) {
+ List<Pair<String, String>> savedImes, String newImeId, String newSubtypeHashCodeStr) {
final StringBuilder builder = new StringBuilder();
boolean isImeAdded = false;
- if (!TextUtils.isEmpty(newImeId) && !TextUtils.isEmpty(newSubtypeId)) {
+ if (!TextUtils.isEmpty(newImeId) && !TextUtils.isEmpty(newSubtypeHashCodeStr)) {
builder.append(newImeId).append(INPUT_METHOD_SUBTYPE_SEPARATOR).append(
- newSubtypeId);
+ newSubtypeHashCodeStr);
isImeAdded = true;
}
for (int i = 0; i < savedImes.size(); ++i) {
final Pair<String, String> ime = savedImes.get(i);
final String imeId = ime.first;
- String subtypeId = ime.second;
- if (TextUtils.isEmpty(subtypeId)) {
- subtypeId = NOT_A_SUBTYPE_ID_STR;
+ String subtypeHashCodeStr = ime.second;
+ if (TextUtils.isEmpty(subtypeHashCodeStr)) {
+ subtypeHashCodeStr = INVALID_SUBTYPE_HASHCODE_STR;
}
if (isImeAdded) {
builder.append(INPUT_METHOD_SEPARATOR);
} else {
isImeAdded = true;
}
- builder.append(imeId).append(INPUT_METHOD_SUBTYPE_SEPARATOR).append(
- subtypeId);
+ builder.append(imeId).append(INPUT_METHOD_SUBTYPE_SEPARATOR).append(subtypeHashCodeStr);
}
// Remove the last INPUT_METHOD_SEPARATOR
putSubtypeHistoryStr(builder.toString());
}
- private void addSubtypeToHistory(String imeId, String subtypeId) {
+ private void addSubtypeToHistory(String imeId, String subtypeHashCodeStr) {
final List<Pair<String, String>> subtypeHistory = loadInputMethodAndSubtypeHistory();
for (int i = 0; i < subtypeHistory.size(); ++i) {
final Pair<String, String> ime = subtypeHistory.get(i);
@@ -301,9 +315,9 @@
}
}
if (DEBUG) {
- Slog.v(TAG, "Add subtype to the history: " + imeId + ", " + subtypeId);
+ Slog.v(TAG, "Add subtype to the history: " + imeId + ", " + subtypeHashCodeStr);
}
- saveSubtypeHistory(subtypeHistory, imeId, subtypeId);
+ saveSubtypeHistory(subtypeHistory, imeId, subtypeHashCodeStr);
}
private void putSubtypeHistoryStr(@NonNull String str) {
@@ -413,26 +427,26 @@
for (int j = 0; j < explicitlyEnabledSubtypes.size(); ++j) {
final String s = explicitlyEnabledSubtypes.get(j);
if (s.equals(subtypeHashCode)) {
- // If both imeId and subtypeId are enabled, return subtypeId.
+ // If both imeId and subtype are enabled, return subtypeId.
try {
final int hashCode = Integer.parseInt(subtypeHashCode);
- // Check whether the subtype id is valid or not
- if (SubtypeUtils.isValidSubtypeId(imi, hashCode)) {
+ // Check whether the subtype is valid or not
+ if (SubtypeUtils.isValidSubtypeHashCode(imi, hashCode)) {
return s;
} else {
- return NOT_A_SUBTYPE_ID_STR;
+ return INVALID_SUBTYPE_HASHCODE_STR;
}
} catch (NumberFormatException e) {
- return NOT_A_SUBTYPE_ID_STR;
+ return INVALID_SUBTYPE_HASHCODE_STR;
}
}
}
}
- // If imeId was enabled but subtypeId was disabled.
- return NOT_A_SUBTYPE_ID_STR;
+ // If imeId was enabled but subtype was disabled.
+ return INVALID_SUBTYPE_HASHCODE_STR;
}
}
- // If both imeId and subtypeId are disabled, return null
+ // If both imeId and subtype are disabled, return null
return null;
}
@@ -451,14 +465,14 @@
String nextImsStr = inputMethodSplitter.next();
subtypeSplitter.setString(nextImsStr);
if (subtypeSplitter.hasNext()) {
- String subtypeId = NOT_A_SUBTYPE_ID_STR;
+ String subtypeHashCodeStr = INVALID_SUBTYPE_HASHCODE_STR;
// The first element is ime id.
String imeId = subtypeSplitter.next();
while (subtypeSplitter.hasNext()) {
- subtypeId = subtypeSplitter.next();
+ subtypeHashCodeStr = subtypeSplitter.next();
break;
}
- imsList.add(new Pair<>(imeId, subtypeId));
+ imsList.add(new Pair<>(imeId, subtypeHashCodeStr));
}
}
return imsList;
@@ -528,13 +542,8 @@
return imi;
}
- boolean isSubtypeSelected() {
- return getSelectedInputMethodSubtypeHashCode() != NOT_A_SUBTYPE_ID;
- }
-
private int getSelectedInputMethodSubtypeHashCode() {
- return getInt(Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE,
- NOT_A_SUBTYPE_ID);
+ return getInt(Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, INVALID_SUBTYPE_HASHCODE);
}
@UserIdInt
@@ -545,7 +554,7 @@
int getSelectedInputMethodSubtypeId(String selectedImiId) {
final InputMethodInfo imi = mMethodMap.get(selectedImiId);
if (imi == null) {
- return NOT_A_SUBTYPE_ID;
+ return InputMethodUtils.NOT_A_SUBTYPE_ID;
}
final int subtypeHashCode = getSelectedInputMethodSubtypeHashCode();
return SubtypeUtils.getSubtypeIdFromHashCode(imi, subtypeHashCode);
@@ -553,12 +562,12 @@
void saveCurrentInputMethodAndSubtypeToHistory(String curMethodId,
InputMethodSubtype currentSubtype) {
- String subtypeId = NOT_A_SUBTYPE_ID_STR;
+ String subtypeHashCodeStr = INVALID_SUBTYPE_HASHCODE_STR;
if (currentSubtype != null) {
- subtypeId = String.valueOf(currentSubtype.hashCode());
+ subtypeHashCodeStr = String.valueOf(currentSubtype.hashCode());
}
if (InputMethodUtils.canAddToLastInputMethod(currentSubtype)) {
- addSubtypeToHistory(curMethodId, subtypeId);
+ addSubtypeToHistory(curMethodId, subtypeHashCodeStr);
}
}
@@ -583,7 +592,7 @@
}
final int subtypeHashCode = getSelectedInputMethodSubtypeHashCode();
- if (subtypeHashCode != NOT_A_SUBTYPE_ID) {
+ if (subtypeHashCode != INVALID_SUBTYPE_HASHCODE) {
final int subtypeIndex = SubtypeUtils.getSubtypeIdFromHashCode(imi,
subtypeHashCode);
if (subtypeIndex >= 0) {
@@ -645,10 +654,10 @@
final IntArray validSubtypeHashCodes = new IntArray(subtypeHashCodes.length);
for (int subtypeHashCode : subtypeHashCodes) {
- if (subtypeHashCode == NOT_A_SUBTYPE_ID) {
- continue; // NOT_A_SUBTYPE_ID must not be saved
+ if (subtypeHashCode == INVALID_SUBTYPE_HASHCODE) {
+ continue; // INVALID_SUBTYPE_HASHCODE must not be saved
}
- if (!SubtypeUtils.isValidSubtypeId(imi, subtypeHashCode)) {
+ if (!SubtypeUtils.isValidSubtypeHashCode(imi, subtypeHashCode)) {
continue; // this subtype does not exist in InputMethodInfo.
}
if (validSubtypeHashCodes.indexOf(subtypeHashCode) >= 0) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
index 23f947c..770e3b2 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -465,11 +465,11 @@
return result;
}
- protected void dump(final Printer pw) {
- pw.println(" mSwitchingAwareRotationList:");
- mSwitchingAwareRotationList.dump(pw, " ");
- pw.println(" mSwitchingUnawareRotationList:");
- mSwitchingUnawareRotationList.dump(pw, " ");
+ protected void dump(@NonNull Printer pw, @NonNull String prefix) {
+ pw.println(prefix + "mSwitchingAwareRotationList:");
+ mSwitchingAwareRotationList.dump(pw, prefix + " ");
+ pw.println(prefix + "mSwitchingUnawareRotationList:");
+ mSwitchingUnawareRotationList.dump(pw, prefix + " ");
}
}
@@ -529,11 +529,11 @@
return mController.getNextInputMethod(onlyCurrentIme, imi, subtype);
}
- public void dump(final Printer pw) {
+ public void dump(@NonNull Printer pw, @NonNull String prefix) {
if (mController != null) {
- mController.dump(pw);
+ mController.dump(pw, prefix);
} else {
- pw.println(" mController=null");
+ pw.println(prefix + "mController=null");
}
}
}
diff --git a/services/core/java/com/android/server/inputmethod/SubtypeUtils.java b/services/core/java/com/android/server/inputmethod/SubtypeUtils.java
index 3d5c8677..1b4c0d6 100644
--- a/services/core/java/com/android/server/inputmethod/SubtypeUtils.java
+++ b/services/core/java/com/android/server/inputmethod/SubtypeUtils.java
@@ -16,9 +16,11 @@
package com.android.server.inputmethod;
+import android.annotation.AnyThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.LocaleList;
+import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Slog;
@@ -100,7 +102,7 @@
return subtypes;
}
- static boolean isValidSubtypeId(InputMethodInfo imi, int subtypeHashCode) {
+ static boolean isValidSubtypeHashCode(InputMethodInfo imi, int subtypeHashCode) {
return getSubtypeIdFromHashCode(imi, subtypeHashCode) != NOT_A_SUBTYPE_ID;
}
@@ -289,4 +291,54 @@
}
return applicableSubtype;
}
+
+ /**
+ * Returns a {@link InputMethodSubtype} available in {@code imi} based on
+ * {@link Settings.Secure#SELECTED_INPUT_METHOD_SUBTYPE}.
+ *
+ * @param imi {@link InputMethodInfo} to find out the current
+ * {@link InputMethodSubtype}
+ * @param settings {@link InputMethodSettings} to be used to find out the current
+ * {@link InputMethodSubtype}
+ * @param currentSubtype the current value that will be used as fallback
+ * @return {@link InputMethodSubtype} to be used as the current {@link InputMethodSubtype}
+ */
+ @AnyThread
+ @Nullable
+ static InputMethodSubtype getCurrentInputMethodSubtype(
+ @NonNull InputMethodInfo imi, @NonNull InputMethodSettings settings,
+ @Nullable InputMethodSubtype currentSubtype) {
+ final int userId = settings.getUserId();
+ final int selectedSubtypeHashCode = SecureSettingsWrapper.getInt(
+ Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, NOT_A_SUBTYPE_ID, userId);
+ if (selectedSubtypeHashCode != NOT_A_SUBTYPE_ID && currentSubtype != null
+ && isValidSubtypeHashCode(imi, currentSubtype.hashCode())) {
+ return currentSubtype;
+ }
+
+ final int subtypeId = settings.getSelectedInputMethodSubtypeId(imi.getId());
+ if (subtypeId != NOT_A_SUBTYPE_ID) {
+ return imi.getSubtypeAt(subtypeId);
+ }
+
+ // If there are no selected subtypes, the framework will try to find the most applicable
+ // subtype from explicitly or implicitly enabled subtypes.
+ final List<InputMethodSubtype> subtypes = settings.getEnabledInputMethodSubtypeList(imi,
+ true);
+ if (subtypes.isEmpty()) {
+ return currentSubtype;
+ }
+ // If there is only one explicitly or implicitly enabled subtype,
+ // just returns it.
+ if (subtypes.size() == 1) {
+ return subtypes.get(0);
+ }
+ final String locale = SystemLocaleWrapper.get(userId).get(0).toString();
+ final var subtype = findLastResortApplicableSubtype(subtypes, SUBTYPE_MODE_KEYBOARD, locale,
+ true);
+ if (subtype != null) {
+ return subtype;
+ }
+ return findLastResortApplicableSubtype(subtypes, null, locale, true);
+ }
}
diff --git a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
index f572845..966be53 100644
--- a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
+++ b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
@@ -32,6 +32,7 @@
import android.util.Slog;
import com.android.internal.widget.VerifyCredentialResponse;
+import com.android.server.biometrics.BiometricHandlerProvider;
import java.util.ArrayList;
import java.util.List;
@@ -132,9 +133,11 @@
mFaceResetLockoutTask = null;
};
- BiometricDeferredQueue(@NonNull SyntheticPasswordManager spManager, @NonNull Handler handler) {
+ BiometricDeferredQueue(@NonNull SyntheticPasswordManager spManager) {
mSpManager = spManager;
- mHandler = handler;
+
+ //Using a higher priority thread to avoid any delays and interruption of clients
+ mHandler = BiometricHandlerProvider.getInstance().getBiometricCallbackHandler();
mPendingResetLockoutsForFingerprint = new ArrayList<>();
mPendingResetLockoutsForFace = new ArrayList<>();
mPendingResetLockouts = new ArrayList<>();
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index ae3d36a..22b33dd 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -687,7 +687,7 @@
mSpManager = injector.getSyntheticPasswordManager(mStorage);
mUnifiedProfilePasswordCache = injector.getUnifiedProfilePasswordCache(mKeyStore);
- mBiometricDeferredQueue = new BiometricDeferredQueue(mSpManager, mHandler);
+ mBiometricDeferredQueue = new BiometricDeferredQueue(mSpManager);
mRebootEscrowManager = injector.getRebootEscrowManager(new RebootEscrowCallbacks(),
mStorage);
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
index d5e85da..3673eb0 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
@@ -57,11 +57,8 @@
import java.util.Objects;
import java.util.Set;
-/**
- * Maintains a connection to a particular {@link MediaRoute2ProviderService}.
- */
-final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
- implements ServiceConnection {
+/** Maintains a connection to a particular {@link MediaRoute2ProviderService}. */
+final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider {
private static final String TAG = "MR2ProviderSvcProxy";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -69,6 +66,7 @@
private final int mUserId;
private final Handler mHandler;
private final boolean mIsSelfScanOnlyProvider;
+ private final ServiceConnection mServiceConnection = new ServiceConnectionImpl();
// Connection state
private boolean mRunning;
@@ -303,9 +301,12 @@
Intent service = new Intent(MediaRoute2ProviderService.SERVICE_INTERFACE);
service.setComponent(mComponentName);
try {
- mBound = mContext.bindServiceAsUser(service, this,
- Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
- new UserHandle(mUserId));
+ mBound =
+ mContext.bindServiceAsUser(
+ service,
+ mServiceConnection,
+ Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
+ new UserHandle(mUserId));
if (!mBound && DEBUG) {
Slog.d(TAG, this + ": Bind failed");
}
@@ -325,12 +326,11 @@
mBound = false;
disconnect();
- mContext.unbindService(this);
+ mContext.unbindService(mServiceConnection);
}
}
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
+ private void onServiceConnectedInternal(IBinder service) {
if (DEBUG) {
Slog.d(TAG, this + ": Connected");
}
@@ -354,16 +354,14 @@
}
}
- @Override
- public void onServiceDisconnected(ComponentName name) {
+ private void onServiceDisconnectedInternal() {
if (DEBUG) {
Slog.d(TAG, this + ": Service disconnected");
}
disconnect();
}
- @Override
- public void onBindingDied(ComponentName name) {
+ private void onBindingDiedInternal(ComponentName name) {
unbind();
if (Flags.enablePreventionOfKeepAliveRouteProviders()) {
Slog.w(
@@ -662,6 +660,37 @@
pendingTransferCount);
}
+ // All methods in this class are called on the main thread.
+ private final class ServiceConnectionImpl implements ServiceConnection {
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ if (Flags.enableMr2ServiceNonMainBgThread()) {
+ mHandler.post(() -> onServiceConnectedInternal(service));
+ } else {
+ onServiceConnectedInternal(service);
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ if (Flags.enableMr2ServiceNonMainBgThread()) {
+ mHandler.post(() -> onServiceDisconnectedInternal());
+ } else {
+ onServiceDisconnectedInternal();
+ }
+ }
+
+ @Override
+ public void onBindingDied(ComponentName name) {
+ if (Flags.enableMr2ServiceNonMainBgThread()) {
+ mHandler.post(() -> onBindingDiedInternal(name));
+ } else {
+ onBindingDiedInternal(name);
+ }
+ }
+ }
+
private final class Connection implements DeathRecipient {
private final IMediaRoute2ProviderService mService;
private final ServiceCallbackStub mCallbackStub;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index f03c639..d9e22c5 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -28,6 +28,7 @@
import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
import static android.app.ActivityManager.PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK;
import static android.app.ActivityManager.PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK;
+import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN;
import static android.app.ActivityManager.isProcStateConsideredInteraction;
import static android.app.ActivityManager.printCapabilitiesSummary;
@@ -523,6 +524,12 @@
*/
private boolean mUseMeteredFirewallChains;
+ /**
+ * Whether or not sensitive process states and non-sensitive process-states have different
+ * delays before network is blocked after transitioning to background.
+ */
+ private boolean mUseDifferentDelaysForBackgroundChain;
+
// See main javadoc for instructions on how to use these locks.
final Object mUidRulesFirstLock = new Object();
final Object mNetworkPoliciesSecondLock = new Object();
@@ -552,10 +559,43 @@
* {@link NetworkPolicyManager#BACKGROUND_THRESHOLD_STATE} will lose network access.
* The delay is meant to prevent churn due to quick process-state changes.
* Note that there is no delay while granting network access.
+ *
+ * This is only used when the flag {@link #mUseDifferentDelaysForBackgroundChain} is disabled.
*/
@VisibleForTesting
long mBackgroundRestrictionDelayMs = TimeUnit.SECONDS.toMillis(5);
+ /**
+ * Short delay after which a uid going into a process state having priority equal to
+ * {@link NetworkPolicyManager#BACKGROUND_THRESHOLD_STATE} or lower will lose network access.
+ *
+ * This will apply to apps that should be fine with losing network access immediately.
+ * It is only meant as a debounce to prevent churn due to quick process-state changes.
+ * Note that there is no delay while granting network access.
+ *
+ * This is only used when the flag {@link #mUseDifferentDelaysForBackgroundChain} is enabled.
+ */
+ @VisibleForTesting
+ long mBackgroundRestrictionShortDelayMs = TimeUnit.SECONDS.toMillis(2);
+
+ /**
+ * Long delay after which a uid going into a process state having priority equal to
+ * {@link NetworkPolicyManager#BACKGROUND_THRESHOLD_STATE} or lower will lose network access.
+ *
+ * Unlike {@link #mBackgroundRestrictionShortDelayMs}, this is meant to be applied to apps
+ * in sensitive proc-states like {@link ActivityManager#PROCESS_STATE_TOP_SLEEPING} and
+ * {@link ActivityManager#PROCESS_STATE_LAST_ACTIVITY}, where the user may switch to this app
+ * before this period and any latency in granting network access before resuming app activities
+ * may degrade experience.
+ *
+ * This is only used when the flag {@link #mUseDifferentDelaysForBackgroundChain} is enabled.
+ */
+ @VisibleForTesting
+ long mBackgroundRestrictionLongDelayMs = TimeUnit.SECONDS.toMillis(20);
+
+ @GuardedBy("mUidRulesFirstLock")
+ private long mNextProcessBackgroundUidsTime = Long.MAX_VALUE;
+
/** Defined network policies. */
@GuardedBy("mNetworkPoliciesSecondLock")
final ArrayMap<NetworkTemplate, NetworkPolicy> mNetworkPolicy = new ArrayMap<>();
@@ -1007,6 +1047,7 @@
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mUseMeteredFirewallChains = Flags.useMeteredFirewallChains();
+ mUseDifferentDelaysForBackgroundChain = Flags.useDifferentDelaysForBackgroundChain();
synchronized (mUidRulesFirstLock) {
synchronized (mNetworkPoliciesSecondLock) {
@@ -1241,11 +1282,21 @@
// different chains may change.
return true;
}
- if (mBackgroundNetworkRestricted && (previousProcState >= BACKGROUND_THRESHOLD_STATE)
+ if (mBackgroundNetworkRestricted) {
+ if ((previousProcState >= BACKGROUND_THRESHOLD_STATE)
!= (newProcState >= BACKGROUND_THRESHOLD_STATE)) {
- // Proc-state change crossed BACKGROUND_THRESHOLD_STATE: Network rules for the
- // BACKGROUND chain may change.
- return true;
+ // Proc-state change crossed BACKGROUND_THRESHOLD_STATE: The network rules will
+ // need to be re-evaluated for the background chain.
+ return true;
+ }
+ if (mUseDifferentDelaysForBackgroundChain
+ && newProcState >= BACKGROUND_THRESHOLD_STATE
+ && getBackgroundTransitioningDelay(newProcState)
+ < getBackgroundTransitioningDelay(previousProcState)) {
+ // The old and new proc-state both are in the blocked state but the background
+ // transition delay is reduced, so we may have to update the rules sooner.
+ return true;
+ }
}
final int networkCapabilities = PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK
| PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK;
@@ -4045,6 +4096,8 @@
+ mBackgroundNetworkRestricted);
fout.println(Flags.FLAG_USE_METERED_FIREWALL_CHAINS + ": "
+ mUseMeteredFirewallChains);
+ fout.println(Flags.FLAG_USE_DIFFERENT_DELAYS_FOR_BACKGROUND_CHAIN + ": "
+ + mUseDifferentDelaysForBackgroundChain);
fout.println();
fout.println("mRestrictBackgroundLowPowerMode: " + mRestrictBackgroundLowPowerMode);
@@ -4188,20 +4241,34 @@
fout.decreaseIndent();
}
- size = mBackgroundTransitioningUids.size();
- if (size > 0) {
- final long nowUptime = SystemClock.uptimeMillis();
- fout.println("Uids transitioning to background:");
- fout.increaseIndent();
- for (int i = 0; i < size; i++) {
- fout.print("UID=");
- fout.print(mBackgroundTransitioningUids.keyAt(i));
- fout.print(", ");
- TimeUtils.formatDuration(mBackgroundTransitioningUids.valueAt(i), nowUptime,
- fout);
+ if (mBackgroundNetworkRestricted) {
+ fout.println();
+ if (mUseDifferentDelaysForBackgroundChain) {
+ fout.print("Background restrictions short delay: ");
+ TimeUtils.formatDuration(mBackgroundRestrictionShortDelayMs, fout);
+ fout.println();
+
+ fout.print("Background restrictions long delay: ");
+ TimeUtils.formatDuration(mBackgroundRestrictionLongDelayMs, fout);
fout.println();
}
- fout.decreaseIndent();
+
+ size = mBackgroundTransitioningUids.size();
+ if (size > 0) {
+ final long nowUptime = SystemClock.uptimeMillis();
+ fout.println("Uids transitioning to background:");
+ fout.increaseIndent();
+ for (int i = 0; i < size; i++) {
+ fout.print("UID=");
+ fout.print(mBackgroundTransitioningUids.keyAt(i));
+ fout.print(", ");
+ TimeUtils.formatDuration(mBackgroundTransitioningUids.valueAt(i),
+ nowUptime, fout);
+ fout.println();
+ }
+ fout.decreaseIndent();
+ }
+ fout.println();
}
final SparseBooleanArray knownUids = new SparseBooleanArray();
@@ -4337,6 +4404,15 @@
|| isProcStateAllowedNetworkWhileBackground(mUidState.get(uid));
}
+ private long getBackgroundTransitioningDelay(int procState) {
+ if (mUseDifferentDelaysForBackgroundChain) {
+ return procState <= PROCESS_STATE_LAST_ACTIVITY ? mBackgroundRestrictionLongDelayMs
+ : mBackgroundRestrictionShortDelayMs;
+ } else {
+ return mBackgroundRestrictionDelayMs;
+ }
+ }
+
/**
* Process state of UID changed; if needed, will trigger
* {@link #updateRulesForDataUsageRestrictionsUL(int)} and
@@ -4387,19 +4463,41 @@
mBackgroundTransitioningUids.delete(uid);
updateRuleForBackgroundUL(uid);
updatePowerRestrictionRules = true;
- } else if (wasAllowed && !isAllowed) {
+ } else if (!isAllowed) {
+ final int transitionIdx = mBackgroundTransitioningUids.indexOfKey(uid);
final long completionTimeMs = SystemClock.uptimeMillis()
- + mBackgroundRestrictionDelayMs;
- if (mBackgroundTransitioningUids.indexOfKey(uid) < 0) {
- // This is just a defensive check in case the upstream code ever makes
- // multiple calls for the same process state change.
- mBackgroundTransitioningUids.put(uid, completionTimeMs);
+ + getBackgroundTransitioningDelay(procState);
+ boolean completionTimeUpdated = false;
+ if (wasAllowed) {
+ // Rules need to transition from allowed to blocked after the respective
+ // delay.
+ if (transitionIdx < 0) {
+ // This is just a defensive check in case the upstream code ever
+ // makes multiple calls for the same process state change.
+ mBackgroundTransitioningUids.put(uid, completionTimeMs);
+ completionTimeUpdated = true;
+ }
+ } else if (mUseDifferentDelaysForBackgroundChain) {
+ // wasAllowed was false, but the transition delay may have reduced.
+ // Currently, this can happen when the uid transitions from
+ // LAST_ACTIVITY to CACHED_ACTIVITY, for example.
+ if (transitionIdx >= 0
+ && completionTimeMs < mBackgroundTransitioningUids.valueAt(
+ transitionIdx)) {
+ mBackgroundTransitioningUids.setValueAt(transitionIdx,
+ completionTimeMs);
+ completionTimeUpdated = true;
+ }
}
- if (!mHandler.hasMessages(MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS)) {
- // Many uids may be in this "transitioning" state at the same time, so
- // using one message at a time to avoid congestion in the MessageQueue.
+ if (completionTimeUpdated
+ && completionTimeMs < mNextProcessBackgroundUidsTime) {
+ // Many uids may be in this "transitioning" state at the same time,
+ // so we always keep one message to process transition completion at
+ // the earliest time.
+ mHandler.removeMessages(MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS);
mHandler.sendEmptyMessageAtTime(
MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS, completionTimeMs);
+ mNextProcessBackgroundUidsTime = completionTimeMs;
}
}
}
@@ -5750,10 +5848,11 @@
updateRuleForBackgroundUL(uid);
updateRulesForPowerRestrictionsUL(uid, false);
}
- }
- if (nextCheckTime < Long.MAX_VALUE) {
- mHandler.sendEmptyMessageAtTime(MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS,
- nextCheckTime);
+ mNextProcessBackgroundUidsTime = nextCheckTime;
+ if (nextCheckTime < Long.MAX_VALUE) {
+ mHandler.sendEmptyMessageAtTime(
+ MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS, nextCheckTime);
+ }
}
return true;
}
diff --git a/services/core/java/com/android/server/net/flags.aconfig b/services/core/java/com/android/server/net/flags.aconfig
index e986dd8..586baf0 100644
--- a/services/core/java/com/android/server/net/flags.aconfig
+++ b/services/core/java/com/android/server/net/flags.aconfig
@@ -17,3 +17,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "use_different_delays_for_background_chain"
+ namespace: "backstage_power"
+ description: "Grant longer grace periods for sensitive process-states before blocking network using the background chain"
+ bug: "323963467"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
old mode 100755
new mode 100644
index 60147ef..a8adc06
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -284,6 +284,7 @@
import android.service.notification.NotificationServiceDumpProto;
import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
+import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeProto;
import android.service.notification.ZenPolicy;
@@ -5508,6 +5509,14 @@
}
@Override
+ public void setManualZenRuleDeviceEffects(ZenDeviceEffects effects) throws RemoteException {
+ checkCallerIsSystem();
+
+ mZenModeHelper.setManualZenRuleDeviceEffects(effects, computeZenOrigin(true),
+ "Update manual mode non-policy settings", Binder.getCallingUid());
+ }
+
+ @Override
public boolean updateAutomaticZenRule(String id, AutomaticZenRule automaticZenRule,
boolean fromUser) throws RemoteException {
validateAutomaticZenRule(id, automaticZenRule);
diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java
index 86aa2d8..02b5f97 100644
--- a/services/core/java/com/android/server/notification/ZenModeConditions.java
+++ b/services/core/java/com/android/server/notification/ZenModeConditions.java
@@ -68,16 +68,17 @@
public void evaluateConfig(ZenModeConfig config, ComponentName trigger,
boolean processSubscriptions) {
if (config == null) return;
- if (config.manualRule != null && config.manualRule.condition != null
+ if (!android.app.Flags.modesUi() && config.manualRule != null
+ && config.manualRule.condition != null
&& !config.manualRule.isTrueOrUnknown()) {
if (DEBUG) Log.d(TAG, "evaluateConfig: clearing manual rule");
config.manualRule = null;
}
final ArraySet<Uri> current = new ArraySet<>();
- evaluateRule(config.manualRule, current, null, processSubscriptions);
+ evaluateRule(config.manualRule, current, null, processSubscriptions, true);
for (ZenRule automaticRule : config.automaticRules.values()) {
if (automaticRule.component != null) {
- evaluateRule(automaticRule, current, trigger, processSubscriptions);
+ evaluateRule(automaticRule, current, trigger, processSubscriptions, false);
updateSnoozing(automaticRule);
}
}
@@ -131,7 +132,7 @@
// Only valid for CPS backed rules
private void evaluateRule(ZenRule rule, ArraySet<Uri> current, ComponentName trigger,
- boolean processSubscriptions) {
+ boolean processSubscriptions, boolean isManual) {
if (rule == null || rule.conditionId == null) return;
if (rule.configurationActivity != null) return;
final Uri id = rule.conditionId;
@@ -153,8 +154,10 @@
}
// empty rule? disable and bail early
if (rule.component == null && rule.enabler == null) {
- Log.w(TAG, "No component found for automatic rule: " + rule.conditionId);
- rule.enabled = false;
+ if (!android.app.Flags.modesUi() || (android.app.Flags.modesUi() && !isManual)) {
+ Log.w(TAG, "No component found for automatic rule: " + rule.conditionId);
+ rule.enabled = false;
+ }
return;
}
if (current != null) {
diff --git a/services/core/java/com/android/server/notification/ZenModeEventLogger.java b/services/core/java/com/android/server/notification/ZenModeEventLogger.java
index 418eacc..ec5d96d 100644
--- a/services/core/java/com/android/server/notification/ZenModeEventLogger.java
+++ b/services/core/java/com/android/server/notification/ZenModeEventLogger.java
@@ -18,8 +18,8 @@
import static android.app.NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND;
import static android.provider.Settings.Global.ZEN_MODE_OFF;
-import static android.service.notification.NotificationServiceProto.CHANNEL_POLICY_PRIORITY;
import static android.service.notification.NotificationServiceProto.CHANNEL_POLICY_NONE;
+import static android.service.notification.NotificationServiceProto.CHANNEL_POLICY_PRIORITY;
import static android.service.notification.NotificationServiceProto.RULE_TYPE_AUTOMATIC;
import static android.service.notification.NotificationServiceProto.RULE_TYPE_MANUAL;
import static android.service.notification.NotificationServiceProto.RULE_TYPE_UNKNOWN;
@@ -352,8 +352,10 @@
ZenModeDiff.RuleDiff manualDiff = diff.getManualRuleDiff();
if (manualDiff != null && manualDiff.hasDiff()) {
// a diff in the manual rule doesn't *necessarily* mean that it's responsible for
- // the change -- only if it's been added or removed.
- if (manualDiff.wasAdded() || manualDiff.wasRemoved()) {
+ // the change -- only if it's been added or removed or updated.
+ if (manualDiff.wasAdded() || manualDiff.wasRemoved()
+ || (Flags.modesUi()
+ && (manualDiff.becameActive() || manualDiff.becameInactive()))) {
return RULE_TYPE_MANUAL;
}
}
@@ -391,10 +393,8 @@
if (config == null) {
return rules;
}
-
- if (config.manualRule != null) {
- // If the manual rule is non-null, then it's active. We make a copy and set the rule
- // type so that the correct value gets logged.
+ if (config.isManualActive()) {
+ // We make a copy and set the rule type so that the correct value gets logged.
ZenRule rule = config.manualRule.copy();
rule.type = ACTIVE_RULE_TYPE_MANUAL;
rules.add(rule);
@@ -592,10 +592,10 @@
// This applies to both call and message senders, but not conversation senders,
// where they use the same enum values.
proto.write(DNDPolicyProto.ALLOW_CALLS_FROM,
- ZenAdapters.notificationPolicySendersToZenPolicyPeopleType(
+ ZenAdapters.prioritySendersToPeopleType(
mNewPolicy.allowCallsFrom()));
proto.write(DNDPolicyProto.ALLOW_MESSAGES_FROM,
- ZenAdapters.notificationPolicySendersToZenPolicyPeopleType(
+ ZenAdapters.prioritySendersToPeopleType(
mNewPolicy.allowMessagesFrom()));
proto.write(DNDPolicyProto.ALLOW_CONVERSATIONS_FROM,
mNewPolicy.allowConversationsFrom());
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 454bd20..267291c 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -24,6 +24,10 @@
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_REMOVED;
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_UNKNOWN;
import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
+import static android.service.notification.Condition.SOURCE_UNKNOWN;
+import static android.service.notification.Condition.SOURCE_USER_ACTION;
+import static android.service.notification.Condition.STATE_FALSE;
+import static android.service.notification.Condition.STATE_TRUE;
import static android.service.notification.NotificationServiceProto.ROOT_CONFIG;
import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_APP;
import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_INIT;
@@ -216,7 +220,9 @@
mAppOps = context.getSystemService(AppOpsManager.class);
mNotificationManager = context.getSystemService(NotificationManager.class);
- mDefaultConfig = readDefaultConfig(mContext.getResources());
+ mDefaultConfig = Flags.modesUi()
+ ? ZenModeConfig.getDefaultConfig()
+ : readDefaultConfig(mContext.getResources());
updateDefaultConfigAutomaticRules();
if (Flags.modesApi()) {
updateDefaultAutomaticRulePolicies();
@@ -612,7 +618,7 @@
if (rule != null) {
Condition deactivated = new Condition(rule.conditionId,
mContext.getString(R.string.zen_mode_implicit_deactivated),
- Condition.STATE_FALSE);
+ STATE_FALSE);
setAutomaticZenRuleStateLocked(newConfig, Collections.singletonList(rule),
deactivated, UPDATE_ORIGIN_APP, callingUid);
}
@@ -627,8 +633,7 @@
// would apply if changing the global interruption filter. We only do this
// for newly created rules, as existing rules have a pre-existing policy
// (whether initialized here or set via app or user).
- rule.zenPolicy = mConfig.toZenPolicy();
-
+ rule.zenPolicy = mConfig.getZenPolicy().copy();
newConfig.automaticRules.put(rule.id, rule);
}
// If the user has changed the rule's *zenMode*, then don't let app overwrite it.
@@ -639,7 +644,7 @@
rule.snoozing = false;
rule.condition = new Condition(rule.conditionId,
mContext.getString(R.string.zen_mode_implicit_activated),
- Condition.STATE_TRUE);
+ STATE_TRUE);
setConfigLocked(newConfig, /* triggeringComponent= */ null, UPDATE_ORIGIN_APP,
"applyGlobalZenModeAsImplicitZenRule", callingUid);
@@ -687,7 +692,7 @@
// would take effect if changing the global policy.
// Note that NotificationManager.Policy cannot have any unset priority
// categories, but *can* have unset visual effects, which is why we do this.
- newZenPolicy = mConfig.toZenPolicy().overwrittenWith(newZenPolicy);
+ newZenPolicy = mConfig.getZenPolicy().overwrittenWith(newZenPolicy);
}
updatePolicy(
rule,
@@ -878,7 +883,7 @@
if (rule == null || !canManageAutomaticZenRule(rule)) {
return Condition.STATE_UNKNOWN;
}
- return rule.condition != null ? rule.condition.state : Condition.STATE_FALSE;
+ return rule.condition != null ? rule.condition.state : STATE_FALSE;
}
}
@@ -929,7 +934,7 @@
Condition condition, @ConfigChangeOrigin int origin, int callingUid) {
if (rules == null || rules.isEmpty()) return;
- if (Flags.modesApi() && condition.source == Condition.SOURCE_USER_ACTION) {
+ if (Flags.modesApi() && condition.source == SOURCE_USER_ACTION) {
origin = UPDATE_ORIGIN_USER; // Although coming from app, it's actually a user action.
}
@@ -1285,7 +1290,7 @@
if (isNew) {
// Newly created rule with no provided policy; fill in with the default.
zenRule.zenPolicy =
- Flags.modesUi() ? mDefaultConfig.toZenPolicy() : mConfig.toZenPolicy();
+ Flags.modesUi() ? mDefaultConfig.getZenPolicy() : mConfig.getZenPolicy();
return true;
}
// Otherwise, a null policy means no policy changes, so we can stop here.
@@ -1296,7 +1301,7 @@
// fields in the bitmask should be marked as updated.
ZenPolicy oldPolicy = zenRule.zenPolicy != null
? zenRule.zenPolicy
- : (Flags.modesUi() ? mDefaultConfig.toZenPolicy() : mConfig.toZenPolicy());
+ : (Flags.modesUi() ? mDefaultConfig.getZenPolicy() : mConfig.getZenPolicy());
// If this is updating a rule rather than creating a new one, keep any fields from the
// old policy if they are unspecified in the new policy. For newly created rules, oldPolicy
@@ -1524,9 +1529,15 @@
+ " conditionId=" + conditionId + " reason=" + reason
+ " setRingerMode=" + setRingerMode);
newConfig = mConfig.copy();
- if (zenMode == Global.ZEN_MODE_OFF) {
- newConfig.manualRule = null;
- if (!Flags.modesUi() || origin != UPDATE_ORIGIN_USER) {
+ if (Flags.modesUi()) {
+ newConfig.manualRule.enabler = caller;
+ newConfig.manualRule.conditionId = conditionId != null ? conditionId : Uri.EMPTY;
+ newConfig.manualRule.pkg = PACKAGE_ANDROID;
+ newConfig.manualRule.zenMode = zenMode;
+ newConfig.manualRule.condition = new Condition(newConfig.manualRule.conditionId, "",
+ zenMode == Global.ZEN_MODE_OFF ? STATE_FALSE : STATE_TRUE,
+ origin == UPDATE_ORIGIN_USER ? SOURCE_USER_ACTION : SOURCE_UNKNOWN);
+ if (zenMode == Global.ZEN_MODE_OFF && origin != UPDATE_ORIGIN_USER) {
// User deactivation of DND means just turning off the manual DND rule.
// For API calls (different origin) keep old behavior of snoozing all rules.
for (ZenRule automaticRule : newConfig.automaticRules.values()) {
@@ -1536,20 +1547,51 @@
}
}
} else {
- final ZenRule newRule = new ZenRule();
- newRule.enabled = true;
- newRule.zenMode = zenMode;
- newRule.conditionId = conditionId;
- newRule.enabler = caller;
- if (Flags.modesApi()) {
- newRule.allowManualInvocation = true;
+ if (zenMode == Global.ZEN_MODE_OFF) {
+ newConfig.manualRule = null;
+ // User deactivation of DND means just turning off the manual DND rule.
+ // For API calls (different origin) keep old behavior of snoozing all rules.
+ for (ZenRule automaticRule : newConfig.automaticRules.values()) {
+ if (automaticRule.isAutomaticActive()) {
+ automaticRule.snoozing = true;
+ }
+ }
+
+ } else {
+ final ZenRule newRule = new ZenRule();
+ newRule.enabled = true;
+ newRule.zenMode = zenMode;
+ newRule.conditionId = conditionId;
+ newRule.enabler = caller;
+ if (Flags.modesApi()) {
+ newRule.allowManualInvocation = true;
+ }
+ newConfig.manualRule = newRule;
}
- newConfig.manualRule = newRule;
}
setConfigLocked(newConfig, origin, reason, null, setRingerMode, callingUid);
}
}
+ public void setManualZenRuleDeviceEffects(ZenDeviceEffects deviceEffects,
+ @ConfigChangeOrigin int origin, String reason, int callingUid) {
+ if (!Flags.modesUi()) {
+ return;
+ }
+ ZenModeConfig newConfig;
+ synchronized (mConfigLock) {
+ if (mConfig == null) return;
+ if (DEBUG) Log.d(TAG, "updateManualRule " + deviceEffects
+ + " reason=" + reason
+ + " callingUid=" + callingUid);
+ newConfig = mConfig.copy();
+
+ newConfig.manualRule.pkg = PACKAGE_ANDROID;
+ newConfig.manualRule.zenDeviceEffects = deviceEffects;
+ setConfigLocked(newConfig, origin, reason, null, true, callingUid);
+ }
+ }
+
void dump(ProtoOutputStream proto) {
proto.write(ZenModeProto.ZEN_MODE, mZenMode);
synchronized (mConfigLock) {
@@ -1558,7 +1600,7 @@
}
for (ZenRule rule : mConfig.automaticRules.values()) {
if (rule.enabled && rule.condition != null
- && rule.condition.state == Condition.STATE_TRUE
+ && rule.condition.state == STATE_TRUE
&& !rule.snoozing) {
rule.dumpDebug(proto, ZenModeProto.ENABLED_ACTIVE_CONDITIONS);
}
@@ -1592,33 +1634,7 @@
private static void dump(PrintWriter pw, String prefix, String var, ZenModeConfig config) {
pw.print(prefix); pw.print(var); pw.print('=');
- if (config == null) {
- pw.println(config);
- return;
- }
- pw.printf("allow(alarms=%b,media=%b,system=%b,calls=%b,callsFrom=%s,repeatCallers=%b,"
- + "messages=%b,messagesFrom=%s,conversations=%b,conversationsFrom=%s,"
- + "events=%b,reminders=%b",
- config.allowAlarms, config.allowMedia, config.allowSystem,
- config.allowCalls, ZenModeConfig.sourceToString(config.allowCallsFrom),
- config.allowRepeatCallers, config.allowMessages,
- ZenModeConfig.sourceToString(config.allowMessagesFrom),
- config.allowConversations,
- ZenPolicy.conversationTypeToString(config.allowConversationsFrom),
- config.allowEvents, config.allowReminders);
- if (Flags.modesApi()) {
- pw.printf(",priorityChannels=%b", config.allowPriorityChannels);
- }
- pw.printf(")\n");
- pw.print(prefix);
- pw.printf(" disallow(visualEffects=%s)\n", config.suppressedVisualEffects);
- pw.print(prefix); pw.print(" manualRule="); pw.println(config.manualRule);
- if (config.automaticRules.isEmpty()) return;
- final int N = config.automaticRules.size();
- for (int i = 0; i < N; i++) {
- pw.print(prefix); pw.print(i == 0 ? " automaticRules=" : " ");
- pw.println(config.automaticRules.valueAt(i));
- }
+ pw.println(config);
}
public void readXml(TypedXmlPullParser parser, boolean forRestore, int userId)
@@ -1629,7 +1645,9 @@
if (config != null) {
if (forRestore) {
config.user = userId;
- config.manualRule = null; // don't restore the manual rule
+ if (!Flags.modesUi()) {
+ config.manualRule = null; // don't restore the manual rule
+ }
}
// booleans to determine whether to reset the rules to the default rules
@@ -1653,7 +1671,7 @@
// rules with null ZenPolicies explicitly as a copy of the global policy.
if (Flags.modesApi() && config.version < ZenModeConfig.XML_VERSION_MODES_API) {
// Keep the manual ("global") policy that from config.
- ZenPolicy manualRulePolicy = config.toZenPolicy();
+ ZenPolicy manualRulePolicy = config.getZenPolicy();
if (automaticRule.zenPolicy == null) {
automaticRule.zenPolicy = manualRulePolicy;
} else {
@@ -1842,7 +1860,7 @@
*/
@VisibleForTesting
protected ZenPolicy getDefaultZenPolicy() {
- return mDefaultConfig.toZenPolicy();
+ return mDefaultConfig.getZenPolicy();
}
@GuardedBy("mConfigLock")
@@ -2008,7 +2026,7 @@
private int computeZenMode() {
synchronized (mConfigLock) {
if (mConfig == null) return Global.ZEN_MODE_OFF;
- if (mConfig.manualRule != null) return mConfig.manualRule.zenMode;
+ if (mConfig.isManualActive()) return mConfig.manualRule.zenMode;
int zen = Global.ZEN_MODE_OFF;
for (ZenRule automaticRule : mConfig.automaticRules.values()) {
if (automaticRule.isAutomaticActive()) {
@@ -2047,19 +2065,19 @@
if (Flags.modesApi()) {
if (useManualConfig) {
// manual rule is configured using the settings stored directly in mConfig
- policy.apply(mConfig.toZenPolicy());
+ policy.apply(mConfig.getZenPolicy());
} else {
// under modes_api flag, an active automatic rule with no specified policy
// inherits the device default settings as stored in mDefaultConfig. While the
// rule's policy fields should be set upon creation, this is a fallback to
// catch any that may have fallen through the cracks.
Log.wtf(TAG, "active automatic rule found with no specified policy: " + rule);
- policy.apply(
- Flags.modesUi() ? mDefaultConfig.toZenPolicy() : mConfig.toZenPolicy());
+ policy.apply(Flags.modesUi()
+ ? mDefaultConfig.getZenPolicy() : mConfig.getZenPolicy());
}
} else {
- // active rule with no specified policy inherits the global config settings
- policy.apply(mConfig.toZenPolicy());
+ // active rule with no specified policy inherits the manual rule config settings
+ policy.apply(mConfig.getZenPolicy());
}
}
}
@@ -2071,7 +2089,7 @@
if (mConfig == null) return;
ZenPolicy policy = new ZenPolicy();
ZenDeviceEffects.Builder deviceEffectsBuilder = new ZenDeviceEffects.Builder();
- if (mConfig.manualRule != null) {
+ if (mConfig.isManualActive()) {
applyCustomPolicy(policy, mConfig.manualRule, true);
if (Flags.modesApi()) {
deviceEffectsBuilder.add(mConfig.manualRule.zenDeviceEffects);
@@ -2154,7 +2172,7 @@
// Should be checked before calling, but just in case.
return;
}
- ZenPolicy defaultPolicy = mDefaultConfig.toZenPolicy();
+ ZenPolicy defaultPolicy = mDefaultConfig.getZenPolicy();
for (ZenRule rule : mDefaultConfig.automaticRules.values()) {
if (ZenModeConfig.DEFAULT_RULE_IDS.contains(rule.id) && rule.zenPolicy == null) {
rule.zenPolicy = defaultPolicy.copy();
@@ -2335,17 +2353,17 @@
final ZenModeConfig config = mConfigs.valueAt(i);
events.add(FrameworkStatsLog.buildStatsEvent(DND_MODE_RULE,
/* optional int32 user = 1 */ user,
- /* optional bool enabled = 2 */ config.manualRule != null,
+ /* optional bool enabled = 2 */ config.isManualActive(),
/* optional bool channels_bypassing = 3 */ config.areChannelsBypassingDnd,
/* optional LoggedZenMode zen_mode = 4 */ ROOT_CONFIG,
/* optional string id = 5 */ "", // empty for root config
/* optional int32 uid = 6 */ Process.SYSTEM_UID, // system owns root config
- /* optional DNDPolicyProto policy = 7 */ config.toZenPolicy().toProto(),
+ /* optional DNDPolicyProto policy = 7 */ config.getZenPolicy().toProto(),
/* optional int32 rule_modified_fields = 8 */ 0,
/* optional int32 policy_modified_fields = 9 */ 0,
/* optional int32 device_effects_modified_fields = 10 */ 0,
/* optional ActiveRuleType rule_type = 11 */ TYPE_UNKNOWN));
- if (config.manualRule != null) {
+ if (config.isManualActive()) {
ruleToProtoLocked(user, config.manualRule, true, events);
}
for (ZenRule rule : config.automaticRules.values()) {
diff --git a/services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java b/services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java
index 96ab2cc..7dd8f2f 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java
+++ b/services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java
@@ -188,7 +188,8 @@
public static IStreamingResponseCallback wrapWithValidation(
IStreamingResponseCallback streamingResponseCallback,
Executor resourceClosingExecutor,
- AndroidFuture future) {
+ AndroidFuture future,
+ InferenceInfoStore inferenceInfoStore) {
return new IStreamingResponseCallback.Stub() {
@Override
public void onNewContent(Bundle processedResult) throws RemoteException {
@@ -207,6 +208,7 @@
sanitizeResponseParams(resultBundle);
streamingResponseCallback.onSuccess(resultBundle);
} finally {
+ inferenceInfoStore.addInferenceInfoFromBundle(resultBundle);
resourceClosingExecutor.execute(() -> tryCloseResource(resultBundle));
future.complete(null);
}
@@ -216,6 +218,7 @@
public void onFailure(int errorCode, String errorMessage,
PersistableBundle errorParams) throws RemoteException {
streamingResponseCallback.onFailure(errorCode, errorMessage, errorParams);
+ inferenceInfoStore.addInferenceInfoFromBundle(errorParams);
future.completeExceptionally(new TimeoutException());
}
@@ -245,7 +248,8 @@
public static IResponseCallback wrapWithValidation(IResponseCallback responseCallback,
Executor resourceClosingExecutor,
- AndroidFuture future) {
+ AndroidFuture future,
+ InferenceInfoStore inferenceInfoStore) {
return new IResponseCallback.Stub() {
@Override
public void onSuccess(Bundle resultBundle)
@@ -254,6 +258,7 @@
sanitizeResponseParams(resultBundle);
responseCallback.onSuccess(resultBundle);
} finally {
+ inferenceInfoStore.addInferenceInfoFromBundle(resultBundle);
resourceClosingExecutor.execute(() -> tryCloseResource(resultBundle));
future.complete(null);
}
@@ -263,6 +268,7 @@
public void onFailure(int errorCode, String errorMessage,
PersistableBundle errorParams) throws RemoteException {
responseCallback.onFailure(errorCode, errorMessage, errorParams);
+ inferenceInfoStore.addInferenceInfoFromBundle(errorParams);
future.completeExceptionally(new TimeoutException());
}
@@ -291,11 +297,13 @@
public static ITokenInfoCallback wrapWithValidation(ITokenInfoCallback responseCallback,
- AndroidFuture future) {
+ AndroidFuture future,
+ InferenceInfoStore inferenceInfoStore) {
return new ITokenInfoCallback.Stub() {
@Override
public void onSuccess(TokenInfo tokenInfo) throws RemoteException {
responseCallback.onSuccess(tokenInfo);
+ inferenceInfoStore.addInferenceInfoFromBundle(tokenInfo.getInfoParams());
future.complete(null);
}
@@ -303,6 +311,7 @@
public void onFailure(int errorCode, String errorMessage, PersistableBundle errorParams)
throws RemoteException {
responseCallback.onFailure(errorCode, errorMessage, errorParams);
+ inferenceInfoStore.addInferenceInfoFromBundle(errorParams);
future.completeExceptionally(new TimeoutException());
}
};
diff --git a/services/core/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java b/services/core/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java
new file mode 100644
index 0000000..6578853
--- /dev/null
+++ b/services/core/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.ondeviceintelligence;
+
+import android.app.ondeviceintelligence.InferenceInfo;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService;
+import android.util.Slog;
+
+import java.io.IOException;
+import java.util.Base64;
+import java.util.Comparator;
+import java.util.List;
+import java.util.TreeSet;
+
+public class InferenceInfoStore {
+ private static final String TAG = "InferenceInfoStore";
+ private final TreeSet<InferenceInfo> inferenceInfos;
+ private final long maxAgeMs;
+
+ public InferenceInfoStore(long maxAgeMs) {
+ this.maxAgeMs = maxAgeMs;
+ this.inferenceInfos = new TreeSet<>(
+ Comparator.comparingLong(InferenceInfo::getStartTimeMs));
+ }
+
+ public List<InferenceInfo> getLatestInferenceInfo(long startTimeEpochMillis) {
+ return inferenceInfos.stream().filter(
+ info -> info.getStartTimeMs() > startTimeEpochMillis).toList();
+ }
+
+ public void addInferenceInfoFromBundle(PersistableBundle pb) {
+ if (!pb.containsKey(OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY)) {
+ return;
+ }
+
+ try {
+ String infoBytesBase64String = pb.getString(
+ OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY);
+ if (infoBytesBase64String != null) {
+ byte[] infoBytes = Base64.getDecoder().decode(infoBytesBase64String);
+ com.android.server.ondeviceintelligence.nano.InferenceInfo inferenceInfo =
+ com.android.server.ondeviceintelligence.nano.InferenceInfo.parseFrom(
+ infoBytes);
+ add(inferenceInfo);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to parse InferenceInfo from the received bytes.");
+ }
+ }
+
+ public void addInferenceInfoFromBundle(Bundle b) {
+ if (!b.containsKey(OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY)) {
+ return;
+ }
+
+ try {
+ byte[] infoBytes = b.getByteArray(
+ OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY);
+ if (infoBytes != null) {
+ com.android.server.ondeviceintelligence.nano.InferenceInfo inferenceInfo =
+ com.android.server.ondeviceintelligence.nano.InferenceInfo.parseFrom(
+ infoBytes);
+ add(inferenceInfo);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to parse InferenceInfo from the received bytes.");
+ }
+ }
+
+ private synchronized void add(com.android.server.ondeviceintelligence.nano.InferenceInfo info) {
+ while (System.currentTimeMillis() - inferenceInfos.first().getStartTimeMs() > maxAgeMs) {
+ inferenceInfos.pollFirst();
+ }
+ inferenceInfos.add(toInferenceInfo(info));
+ }
+
+ private static InferenceInfo toInferenceInfo(
+ com.android.server.ondeviceintelligence.nano.InferenceInfo info) {
+ return new InferenceInfo.Builder().setUid(info.uid).setStartTimeMs(
+ info.startTimeMs).setEndTimeMs(info.endTimeMs).setSuspendedTimeMs(
+ info.suspendedTimeMs).build();
+ }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerInternal.java b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerInternal.java
index 07af8d0..1450dc0 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerInternal.java
+++ b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerInternal.java
@@ -17,5 +17,10 @@
package com.android.server.ondeviceintelligence;
public interface OnDeviceIntelligenceManagerInternal {
+ /**
+ * Gets the uid for the process that is currently hosting the
+ * {@link android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService} registered on
+ * the device.
+ */
int getInferenceServiceUid();
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
index 59964e0..9ef2e12 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
+++ b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
@@ -44,6 +44,7 @@
import android.app.ondeviceintelligence.IResponseCallback;
import android.app.ondeviceintelligence.IStreamingResponseCallback;
import android.app.ondeviceintelligence.ITokenInfoCallback;
+import android.app.ondeviceintelligence.InferenceInfo;
import android.app.ondeviceintelligence.OnDeviceIntelligenceException;
import android.content.ComponentName;
import android.content.Context;
@@ -127,6 +128,7 @@
private static final String NAMESPACE_ON_DEVICE_INTELLIGENCE = "ondeviceintelligence";
private static final String SYSTEM_PACKAGE = "android";
+ private static final long MAX_AGE_MS = TimeUnit.HOURS.toMillis(3);
private final Executor resourceClosingExecutor = Executors.newCachedThreadPool();
@@ -138,7 +140,7 @@
private final Context mContext;
protected final Object mLock = new Object();
-
+ private final InferenceInfoStore mInferenceInfoStore;
private RemoteOnDeviceSandboxedInferenceService mRemoteInferenceService;
private RemoteOnDeviceIntelligenceService mRemoteOnDeviceIntelligenceService;
volatile boolean mIsServiceEnabled;
@@ -170,6 +172,7 @@
super(context);
mContext = context;
mTemporaryServiceNames = new String[0];
+ mInferenceInfoStore = new InferenceInfoStore(MAX_AGE_MS);
}
@Override
@@ -191,6 +194,16 @@
mIsServiceEnabled = isServiceEnabled();
}
+
+ //connect to remote services(if available) during boot phase.
+ if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
+ try {
+ ensureRemoteInferenceServiceInitialized();
+ ensureRemoteIntelligenceServiceInitialized();
+ } catch (Exception e) {
+ Slog.w(TAG, "Couldn't pre-start remote ondeviceintelligence services.", e);
+ }
+ }
}
private void onDeviceConfigChange(@NonNull Set<String> keys) {
@@ -213,10 +226,18 @@
}
@Override
+ public List<InferenceInfo> getLatestInferenceInfo(long startTimeEpochMillis) {
+ mContext.enforceCallingPermission(
+ Manifest.permission.DUMP, TAG);
+ return OnDeviceIntelligenceManagerService.this.getLatestInferenceInfo(
+ startTimeEpochMillis);
+ }
+
+ @Override
public void getVersion(RemoteCallback remoteCallback) {
Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getVersion");
Objects.requireNonNull(remoteCallback);
- mContext.enforceCallingOrSelfPermission(
+ mContext.enforceCallingPermission(
Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
if (!mIsServiceEnabled) {
Slog.w(TAG, "Service not available");
@@ -241,7 +262,7 @@
throws RemoteException {
Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getFeatures");
Objects.requireNonNull(featureCallback);
- mContext.enforceCallingOrSelfPermission(
+ mContext.enforceCallingPermission(
Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
if (!mIsServiceEnabled) {
Slog.w(TAG, "Service not available");
@@ -279,7 +300,7 @@
throws RemoteException {
Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getFeatures");
Objects.requireNonNull(listFeaturesCallback);
- mContext.enforceCallingOrSelfPermission(
+ mContext.enforceCallingPermission(
Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
if (!mIsServiceEnabled) {
Slog.w(TAG, "Service not available");
@@ -323,7 +344,7 @@
Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getFeatureStatus");
Objects.requireNonNull(feature);
Objects.requireNonNull(featureDetailsCallback);
- mContext.enforceCallingOrSelfPermission(
+ mContext.enforceCallingPermission(
Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
if (!mIsServiceEnabled) {
Slog.w(TAG, "Service not available");
@@ -367,7 +388,7 @@
Slog.i(TAG, "OnDeviceIntelligenceManagerInternal requestFeatureDownload");
Objects.requireNonNull(feature);
Objects.requireNonNull(downloadCallback);
- mContext.enforceCallingOrSelfPermission(
+ mContext.enforceCallingPermission(
Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
if (!mIsServiceEnabled) {
Slog.w(TAG, "Service not available");
@@ -407,7 +428,7 @@
sanitizeInferenceParams(request);
Objects.requireNonNull(tokenInfoCallback);
- mContext.enforceCallingOrSelfPermission(
+ mContext.enforceCallingPermission(
Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
if (!mIsServiceEnabled) {
Slog.w(TAG, "Service not available");
@@ -424,7 +445,8 @@
service.requestTokenInfo(callerUid, feature,
request,
wrapCancellationFuture(cancellationSignalFuture),
- wrapWithValidation(tokenInfoCallback, future));
+ wrapWithValidation(tokenInfoCallback, future,
+ mInferenceInfoStore));
return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
});
result.whenCompleteAsync((c, e) -> BundleUtil.tryCloseResource(request),
@@ -450,7 +472,7 @@
Objects.requireNonNull(feature);
sanitizeInferenceParams(request);
Objects.requireNonNull(responseCallback);
- mContext.enforceCallingOrSelfPermission(
+ mContext.enforceCallingPermission(
Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
if (!mIsServiceEnabled) {
Slog.w(TAG, "Service not available");
@@ -470,7 +492,8 @@
wrapCancellationFuture(cancellationSignalFuture),
wrapProcessingFuture(processingSignalFuture),
wrapWithValidation(responseCallback,
- resourceClosingExecutor, future));
+ resourceClosingExecutor, future,
+ mInferenceInfoStore));
return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
});
result.whenCompleteAsync((c, e) -> BundleUtil.tryCloseResource(request),
@@ -495,7 +518,7 @@
Objects.requireNonNull(feature);
sanitizeInferenceParams(request);
Objects.requireNonNull(streamingCallback);
- mContext.enforceCallingOrSelfPermission(
+ mContext.enforceCallingPermission(
Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
if (!mIsServiceEnabled) {
Slog.w(TAG, "Service not available");
@@ -515,7 +538,8 @@
wrapCancellationFuture(cancellationSignalFuture),
wrapProcessingFuture(processingSignalFuture),
wrapWithValidation(streamingCallback,
- resourceClosingExecutor, future));
+ resourceClosingExecutor, future,
+ mInferenceInfoStore));
return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
});
result.whenCompleteAsync((c, e) -> BundleUtil.tryCloseResource(request),
@@ -836,6 +860,10 @@
&& (serviceInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) == 0;
}
+ private List<InferenceInfo> getLatestInferenceInfo(long startTimeEpochMillis) {
+ return mInferenceInfoStore.getLatestInferenceInfo(startTimeEpochMillis);
+ }
+
@Nullable
public String getRemoteConfiguredPackageName() {
try {
@@ -1056,7 +1084,7 @@
}
private void setRemoteInferenceServiceUid(int remoteInferenceServiceUid) {
- synchronized (mLock){
+ synchronized (mLock) {
this.remoteInferenceServiceUid = remoteInferenceServiceUid;
}
}
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index 209cbb7..e34bdc6 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -728,7 +728,14 @@
final int compilationReason =
dexManager.getCompilationReasonForInstallScenario(
installRequest.getInstallScenario());
- return new DexoptOptions(packageName, compilationReason, dexoptFlags);
+ final AndroidPackage pkg = ps.getPkg();
+ var options = new DexoptOptions(packageName, compilationReason, dexoptFlags);
+ if (installRequest.getDexoptCompilerFilter() != null) {
+ options = options.overrideCompilerFilter(installRequest.getDexoptCompilerFilter());
+ } else if (pkg != null && pkg.isDebuggable()) {
+ options = options.overrideCompilerFilter(DexoptParams.COMPILER_FILTER_NOOP);
+ }
+ return options;
}
/**
@@ -772,12 +779,12 @@
&& installRequest.getInstallSource().mInitiatingPackageName.equals("android"))
: true;
+ // Don't skip the dexopt call if the compiler filter is "skip". Instead, call dexopt with
+ // the "skip" filter so that ART Service gets notified and skips dexopt itself.
return (!instantApp || Global.getInt(context.getContentResolver(),
Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
&& pkg != null
- && !pkg.isDebuggable()
&& (!onIncremental)
- && dexoptOptions.isCompilationEnabled()
&& !isApex
&& performDexOptForRollback;
}
diff --git a/services/core/java/com/android/server/pm/InstallArgs.java b/services/core/java/com/android/server/pm/InstallArgs.java
index 46f9732..8001615 100644
--- a/services/core/java/com/android/server/pm/InstallArgs.java
+++ b/services/core/java/com/android/server/pm/InstallArgs.java
@@ -58,6 +58,8 @@
final int mDataLoaderType;
final int mPackageSource;
final boolean mApplicationEnabledSettingPersistent;
+ @Nullable
+ final String mDexoptCompilerFilter;
// The list of instruction sets supported by this app. This is currently
// only used during the rmdex() phase to clean up resources. We can get rid of this
@@ -73,7 +75,7 @@
int autoRevokePermissionsMode, String traceMethod, int traceCookie,
SigningDetails signingDetails, int installReason, int installScenario,
boolean forceQueryableOverride, int dataLoaderType, int packageSource,
- boolean applicationEnabledSettingPersistent) {
+ boolean applicationEnabledSettingPersistent, String dexoptCompilerFilter) {
mOriginInfo = originInfo;
mMoveInfo = moveInfo;
mInstallFlags = installFlags;
@@ -96,5 +98,6 @@
mDataLoaderType = dataLoaderType;
mPackageSource = packageSource;
mApplicationEnabledSettingPersistent = applicationEnabledSettingPersistent;
+ mDexoptCompilerFilter = dexoptCompilerFilter;
}
}
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index 8f51e36..dd2583a0d 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -174,13 +174,13 @@
mUserId = params.getUser().getIdentifier();
mInstallArgs = new InstallArgs(params.mOriginInfo, params.mMoveInfo, params.mObserver,
params.mInstallFlags, params.mDevelopmentInstallFlags, params.mInstallSource,
- params.mVolumeUuid, params.getUser(), null /*instructionSets*/,
+ params.mVolumeUuid, params.getUser(), null /*instructionSets*/,
params.mPackageAbiOverride, params.mPermissionStates,
params.mAllowlistedRestrictedPermissions, params.mAutoRevokePermissionsMode,
params.mTraceMethod, params.mTraceCookie, params.mSigningDetails,
params.mInstallReason, params.mInstallScenario, params.mForceQueryableOverride,
params.mDataLoaderType, params.mPackageSource,
- params.mApplicationEnabledSettingPersistent);
+ params.mApplicationEnabledSettingPersistent, params.mDexoptCompilerFilter);
mPackageLite = params.mPackageLite;
mPackageMetrics = new PackageMetrics(this);
mIsInstallInherit = params.mIsInherit;
@@ -709,6 +709,11 @@
return mWarnings;
}
+ @Nullable
+ public String getDexoptCompilerFilter() {
+ return mInstallArgs != null ? mInstallArgs.mDexoptCompilerFilter : null;
+ }
+
public void setScanFlags(int scanFlags) {
mScanFlags = scanFlags;
}
diff --git a/services/core/java/com/android/server/pm/InstallingSession.java b/services/core/java/com/android/server/pm/InstallingSession.java
index d3a18f9..ccc1175 100644
--- a/services/core/java/com/android/server/pm/InstallingSession.java
+++ b/services/core/java/com/android/server/pm/InstallingSession.java
@@ -102,6 +102,7 @@
@Nullable
final DomainSet mPreVerifiedDomains;
final boolean mHasAppMetadataFile;
+ @Nullable final String mDexoptCompilerFilter;
// For move install
InstallingSession(OriginInfo originInfo, MoveInfo moveInfo, IPackageInstallObserver2 observer,
@@ -136,6 +137,7 @@
mApplicationEnabledSettingPersistent = false;
mPreVerifiedDomains = null;
mHasAppMetadataFile = false;
+ mDexoptCompilerFilter = null;
}
InstallingSession(int sessionId, File stagedDir, IPackageInstallObserver2 observer,
@@ -172,6 +174,7 @@
mApplicationEnabledSettingPersistent = sessionParams.applicationEnabledSettingPersistent;
mPreVerifiedDomains = preVerifiedDomains;
mHasAppMetadataFile = hasAppMetadatafile;
+ mDexoptCompilerFilter = sessionParams.dexoptCompilerFilter;
}
@Override
diff --git a/services/core/java/com/android/server/pm/KillAppBlocker.java b/services/core/java/com/android/server/pm/KillAppBlocker.java
index e2901c3..7f5ad9d 100644
--- a/services/core/java/com/android/server/pm/KillAppBlocker.java
+++ b/services/core/java/com/android/server/pm/KillAppBlocker.java
@@ -83,13 +83,13 @@
}
}
- void waitAppProcessGone(ActivityManagerInternal mAmi, Computer snapshot,
+ void waitAppProcessGone(ActivityManagerInternal ami, Computer snapshot,
UserManagerService userManager, String packageName) {
if (!mRegistered) {
return;
}
synchronized (this) {
- if (mAmi != null) {
+ if (ami != null) {
int[] users = userManager.getUserIds();
for (int i = 0; i < users.length; i++) {
@@ -97,12 +97,16 @@
final int uid = snapshot.getPackageUidInternal(
packageName, MATCH_ALL, userId, Process.SYSTEM_UID);
if (uid != INVALID_UID) {
- if (mAmi.getUidProcessState(uid) != PROCESS_STATE_NONEXISTENT) {
+ if (ami.getUidProcessState(uid) != PROCESS_STATE_NONEXISTENT) {
mActiveUids.add(uid);
}
}
}
}
+ if (mActiveUids.size() == 0) {
+ // no active uid
+ return;
+ }
}
try {
diff --git a/services/core/java/com/android/server/pm/PackageFreezer.java b/services/core/java/com/android/server/pm/PackageFreezer.java
index 0afda45..11f2059 100644
--- a/services/core/java/com/android/server/pm/PackageFreezer.java
+++ b/services/core/java/com/android/server/pm/PackageFreezer.java
@@ -62,6 +62,12 @@
PackageFreezer(String packageName, int userId, String killReason,
PackageManagerService pm, int exitInfoReason, @Nullable InstallRequest request) {
+ this(packageName, userId, killReason, pm, exitInfoReason, request, false);
+ }
+
+ PackageFreezer(String packageName, int userId, String killReason,
+ PackageManagerService pm, int exitInfoReason, @Nullable InstallRequest request,
+ boolean waitAppKilled) {
mPm = pm;
mPackageName = packageName;
mInstallRequest = request;
@@ -77,7 +83,7 @@
ps = mPm.mSettings.getPackageLPr(mPackageName);
}
if (ps != null) {
- if (Flags.waitApplicationKilled()) {
+ if (waitAppKilled && Flags.waitApplicationKilled()) {
mPm.killApplicationSync(ps.getPackageName(), ps.getAppId(), userId, killReason,
exitInfoReason);
} else {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 050d44e..b93dcdc 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -1423,7 +1423,9 @@
DevicePolicyManagerInternal dpmi =
LocalServices.getService(DevicePolicyManagerInternal.class);
final boolean canSilentlyInstallPackage =
- dpmi != null && dpmi.canSilentlyInstallPackage(callerPackageName, callingUid);
+ (dpmi != null && dpmi.canSilentlyInstallPackage(callerPackageName, callingUid))
+ || PackageInstallerSession.isEmergencyInstallerEnabled(
+ versionedPackage.getPackageName(), snapshot, userId, callingUid);
final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
statusReceiver, versionedPackage.getPackageName(),
@@ -1445,15 +1447,6 @@
.createEvent(DevicePolicyEnums.UNINSTALL_PACKAGE)
.setAdmin(callerPackageName)
.write();
- } else if (PackageInstallerSession.isEmergencyInstallerEnabled(callerPackageName, snapshot,
- userId, callingUid)) {
- // Need to clear the calling identity to get DELETE_PACKAGES permission
- final long ident = Binder.clearCallingIdentity();
- try {
- mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
} else {
ApplicationInfo appInfo = snapshot.getApplicationInfo(callerPackageName, 0, userId);
if (appInfo.targetSdkVersion >= Build.VERSION_CODES.P) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ca84d68..66a93d7 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4347,7 +4347,14 @@
public PackageFreezer freezePackage(String packageName, int userId, String killReason,
int exitInfoReason, InstallRequest request) {
- return new PackageFreezer(packageName, userId, killReason, this, exitInfoReason, request);
+ return freezePackage(packageName, userId, killReason, exitInfoReason, request,
+ /* waitAppKilled= */ false);
+ }
+
+ private PackageFreezer freezePackage(String packageName, int userId, String killReason,
+ int exitInfoReason, InstallRequest request, boolean waitAppKilled) {
+ return new PackageFreezer(packageName, userId, killReason, this, exitInfoReason, request,
+ waitAppKilled);
}
public PackageFreezer freezePackageForDelete(String packageName, int userId, int deleteFlags,
@@ -4772,7 +4779,8 @@
final boolean succeeded;
try (PackageFreezer freezer = freezePackage(packageName, UserHandle.USER_ALL,
"clearApplicationUserData",
- ApplicationExitInfo.REASON_USER_REQUESTED, null /* request */)) {
+ ApplicationExitInfo.REASON_USER_REQUESTED, null /* request */,
+ /* waitAppKilled= */ true)) {
try (PackageManagerTracedLock installLock = mInstallLock.acquireLock()) {
succeeded = clearApplicationUserDataLIF(snapshotComputer(), packageName,
userId);
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
index e2ddba5..819a75c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
@@ -18,7 +18,7 @@
import android.os.SystemProperties;
-import com.android.server.pm.dex.DexoptOptions;
+import com.android.server.art.model.DexoptParams;
import dalvik.system.DexFile;
@@ -71,7 +71,7 @@
private static String getAndCheckValidity(int reason) {
String sysPropValue = SystemProperties.get(getSystemPropertyName(reason));
if (sysPropValue == null || sysPropValue.isEmpty()
- || !(sysPropValue.equals(DexoptOptions.COMPILER_FILTER_NOOP)
+ || !(sysPropValue.equals(DexoptParams.COMPILER_FILTER_NOOP)
|| DexFile.isValidCompilerFilter(sysPropValue))) {
throw new IllegalStateException("Value \"" + sysPropValue +"\" not valid "
+ "(reason " + REASON_STRINGS[reason] + ")");
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index a876616..7a53fe7 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -121,6 +121,8 @@
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
import com.android.server.art.ArtManagerLocal;
+import com.android.server.art.ReasonMapping;
+import com.android.server.art.model.DexoptParams;
import com.android.server.pm.PackageManagerShellCommandDataLoader.Metadata;
import com.android.server.pm.permission.LegacyPermissionManagerInternal;
import com.android.server.pm.permission.PermissionAllowlist;
@@ -3589,6 +3591,14 @@
case "--package-source":
sessionParams.setPackageSource(Integer.parseInt(getNextArg()));
break;
+ case "--dexopt-compiler-filter":
+ sessionParams.dexoptCompilerFilter = getNextArgRequired();
+ // An early check that throws IllegalArgumentException if the compiler filter is
+ // invalid.
+ new DexoptParams.Builder(ReasonMapping.REASON_INSTALL)
+ .setCompilerFilter(sessionParams.dexoptCompilerFilter)
+ .build();
+ break;
default:
throw new IllegalArgumentException("Unknown option " + opt);
}
@@ -4735,6 +4745,7 @@
pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES]");
pw.println(" [--apex] [--non-staged] [--force-non-staged]");
pw.println(" [--staged-ready-timeout TIMEOUT] [--ignore-dexopt-profile]");
+ pw.println(" [--dexopt-compiler-filter FILTER]");
pw.println(" [PATH [SPLIT...]|-]");
pw.println(" Install an application. Must provide the apk data to install, either as");
pw.println(" file path(s) or '-' to read from stdin. Options are:");
@@ -4781,13 +4792,19 @@
pw.println(" milliseconds for pre-reboot verification to complete when");
pw.println(" performing staged install. This flag is used to alter the waiting");
pw.println(" time. You can skip the waiting time by specifying a TIMEOUT of '0'");
- pw.println(" --ignore-dexopt-profile: If set, all profiles are ignored by dexopt");
+ pw.println(" --ignore-dexopt-profile: if set, all profiles are ignored by dexopt");
pw.println(" during the installation, including the profile in the DM file and");
pw.println(" the profile embedded in the APK file. If an invalid profile is");
pw.println(" provided during installation, no warning will be reported by `adb");
pw.println(" install`.");
pw.println(" This option does not affect later dexopt operations (e.g.,");
pw.println(" background dexopt and manual `pm compile` invocations).");
+ pw.println(" --dexopt-compiler-filter: the target compiler filter for dexopt during");
+ pw.println(" the installation. The filter actually used may be different.");
+ pw.println(" Valid values: one of the values documented in");
+ pw.println(" https://source.android.com/docs/core/runtime/configure"
+ + "#compiler_filters");
+ pw.println(" or 'skip'");
pw.println("");
pw.println(" install-existing [--user USER_ID|all|current]");
pw.println(" [--instant] [--full] [--wait] [--restrict-permissions] PACKAGE");
diff --git a/services/core/java/com/android/server/pm/PackageMetrics.java b/services/core/java/com/android/server/pm/PackageMetrics.java
index 2081f73..0acadb1 100644
--- a/services/core/java/com/android/server/pm/PackageMetrics.java
+++ b/services/core/java/com/android/server/pm/PackageMetrics.java
@@ -16,7 +16,12 @@
package com.android.server.pm;
+import static android.content.pm.PackageManager.GET_RESOLVED_FILTER;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
import static android.os.Process.INVALID_UID;
+import static android.os.Process.SYSTEM_UID;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -25,6 +30,7 @@
import android.app.ActivityManager;
import android.app.admin.SecurityLog;
import android.content.ComponentName;
+import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.Flags;
import android.content.pm.PackageManager;
@@ -376,7 +382,30 @@
mCallingUid = callingUid;
}
- public boolean isSameComponent(ActivityInfo activityInfo) {
+ public boolean isLauncherActivity(@NonNull Computer computer, @UserIdInt int userId) {
+ if (mIsForWholeApp) {
+ return false;
+ }
+ // Query the launcher activities with the package name.
+ final Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.addCategory(Intent.CATEGORY_LAUNCHER);
+ intent.setPackage(mPackageName);
+ List<ResolveInfo> launcherActivities = computer.queryIntentActivitiesInternal(
+ intent, null,
+ MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | GET_RESOLVED_FILTER
+ | MATCH_DISABLED_COMPONENTS, SYSTEM_UID, userId);
+ final int launcherActivitiesSize =
+ launcherActivities != null ? launcherActivities.size() : 0;
+ for (int i = 0; i < launcherActivitiesSize; i++) {
+ ResolveInfo resolveInfo = launcherActivities.get(i);
+ if (isSameComponent(resolveInfo.activityInfo)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isSameComponent(ActivityInfo activityInfo) {
if (activityInfo == null) {
return false;
}
@@ -395,25 +424,13 @@
Slog.d(TAG, "Fail to report component state due to metrics is empty");
return;
}
- boolean isLauncher = false;
- final List<ResolveInfo> resolveInfosForLauncher = getHomeActivitiesResolveInfoAsUser(
- computer, userId);
- final int resolveInfosForLauncherSize =
- resolveInfosForLauncher != null ? resolveInfosForLauncher.size() : 0;
final int metricsSize = componentStateMetricsList.size();
for (int i = 0; i < metricsSize; i++) {
final ComponentStateMetrics componentStateMetrics = componentStateMetricsList.get(i);
- for (int j = 0; j < resolveInfosForLauncherSize; j++) {
- ResolveInfo resolveInfo = resolveInfosForLauncher.get(j);
- if (componentStateMetrics.isSameComponent(resolveInfo.activityInfo)) {
- isLauncher = true;
- break;
- }
- }
reportComponentStateChanged(componentStateMetrics.mUid,
componentStateMetrics.mComponentOldState,
componentStateMetrics.mComponentNewState,
- isLauncher,
+ componentStateMetrics.isLauncherActivity(computer, userId),
componentStateMetrics.mIsForWholeApp,
componentStateMetrics.mCallingUid);
}
@@ -424,10 +441,4 @@
FrameworkStatsLog.write(FrameworkStatsLog.COMPONENT_STATE_CHANGED_REPORTED,
uid, componentOldState, componentNewState, isLauncher, isForWholeApp, callingUid);
}
-
- private static List<ResolveInfo> getHomeActivitiesResolveInfoAsUser(@NonNull Computer computer,
- @UserIdInt int userId) {
- return computer.queryIntentActivitiesInternal(computer.getHomeIntent(), /* resolvedType */
- null, /* flags */ 0, userId);
- }
}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 78d8002..1cd77ff 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -320,10 +320,10 @@
private final Handler mHandler;
- @GuardedBy("itself")
+ @GuardedBy("mServiceLock")
private final ArrayList<ShortcutChangeListener> mListeners = new ArrayList<>(1);
- @GuardedBy("itself")
+ @GuardedBy("mServiceLock")
private final ArrayList<LauncherApps.ShortcutChangeCallback> mShortcutChangeCallbacks =
new ArrayList<>(1);
@@ -1847,9 +1847,7 @@
return;
}
- synchronized (mListeners) {
- copy = new ArrayList<>(mListeners);
- }
+ copy = new ArrayList<>(mListeners);
}
// Note onShortcutChanged() needs to be called with the system service permissions.
for (int i = copy.size() - 1; i >= 0; i--) {
@@ -1874,9 +1872,8 @@
if (!isUserUnlockedL(userId)) {
return;
}
- synchronized (mShortcutChangeCallbacks) {
- copy = new ArrayList<>(mShortcutChangeCallbacks);
- }
+
+ copy = new ArrayList<>(mShortcutChangeCallbacks);
}
for (int i = copy.size() - 1; i >= 0; i--) {
if (!CollectionUtils.isEmpty(changedList)) {
@@ -3432,7 +3429,7 @@
@Override
public void addListener(@NonNull ShortcutChangeListener listener) {
- synchronized (mListeners) {
+ synchronized (mServiceLock) {
mListeners.add(Objects.requireNonNull(listener));
}
}
@@ -3440,7 +3437,7 @@
@Override
public void addShortcutChangeCallback(
@NonNull LauncherApps.ShortcutChangeCallback callback) {
- synchronized (mShortcutChangeCallbacks) {
+ synchronized (mServiceLock) {
mShortcutChangeCallbacks.add(Objects.requireNonNull(callback));
}
}
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 483d308..95e5b84 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -156,7 +156,8 @@
UserManager.DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO,
UserManager.DISALLOW_SIM_GLOBALLY,
UserManager.DISALLOW_ASSIST_CONTENT,
- UserManager.DISALLOW_THREAD_NETWORK
+ UserManager.DISALLOW_THREAD_NETWORK,
+ UserManager.DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO
});
public static final Set<String> DEPRECATED_USER_RESTRICTIONS = Sets.newArraySet(
@@ -208,7 +209,8 @@
UserManager.DISALLOW_CELLULAR_2G,
UserManager.DISALLOW_ULTRA_WIDEBAND_RADIO,
UserManager.DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO,
- UserManager.DISALLOW_THREAD_NETWORK
+ UserManager.DISALLOW_THREAD_NETWORK,
+ UserManager.DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO
);
/**
@@ -254,7 +256,8 @@
UserManager.DISALLOW_CELLULAR_2G,
UserManager.DISALLOW_ULTRA_WIDEBAND_RADIO,
UserManager.DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO,
- UserManager.DISALLOW_THREAD_NETWORK
+ UserManager.DISALLOW_THREAD_NETWORK,
+ UserManager.DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO
);
/**
diff --git a/services/core/java/com/android/server/pm/dex/DexoptOptions.java b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
index fcdc008..8cf248d 100644
--- a/services/core/java/com/android/server/pm/dex/DexoptOptions.java
+++ b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
@@ -73,12 +73,6 @@
// or device setup and should be scheduled appropriately.
public static final int DEXOPT_FOR_RESTORE = 1 << 11; // TODO(b/135202722): remove
- /**
- * A value indicating that dexopt shouldn't be run. This string is only used when loading
- * filters from the `pm.dexopt.install*` properties and is not propagated to dex2oat.
- */
- public static final String COMPILER_FILTER_NOOP = "skip";
-
// The name of package to optimize.
private final String mPackageName;
@@ -186,10 +180,6 @@
return mCompilationReason;
}
- public boolean isCompilationEnabled() {
- return !mCompilerFilter.equals(COMPILER_FILTER_NOOP);
- }
-
/**
* Creates a new set of DexoptOptions which are the same with the exception of the compiler
* filter (set to the given value).
diff --git a/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java b/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java
index 3edd697..f518769 100644
--- a/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java
+++ b/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java
@@ -38,6 +38,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.function.DodecFunction;
+import com.android.internal.util.function.HexConsumer;
import com.android.internal.util.function.HexFunction;
import com.android.internal.util.function.OctFunction;
import com.android.internal.util.function.QuadFunction;
@@ -269,8 +270,8 @@
if (isDelegatePermission(permissionName)) {
final long identity = Binder.clearCallingIdentity();
try {
- return checkPermission(SHELL_PKG, permissionName,
- persistentDeviceId, userId, superImpl);
+ return checkPermission(SHELL_PKG, permissionName, persistentDeviceId,
+ userId, superImpl);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -323,8 +324,7 @@
Process.SHELL_UID);
final long identity = Binder.clearCallingIdentity();
try {
- return superImpl.apply(code, shellUid, "com.android.shell", null,
- virtualDeviceId, raw);
+ return superImpl.apply(code, shellUid, SHELL_PKG, null, virtualDeviceId, raw);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -340,7 +340,7 @@
Process.SHELL_UID);
final long identity = Binder.clearCallingIdentity();
try {
- return superImpl.apply(code, usage, shellUid, "com.android.shell");
+ return superImpl.apply(code, usage, shellUid, SHELL_PKG);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -359,9 +359,8 @@
Process.SHELL_UID);
final long identity = Binder.clearCallingIdentity();
try {
- return superImpl.apply(code, shellUid, "com.android.shell", featureId,
- virtualDeviceId, shouldCollectAsyncNotedOp, message,
- shouldCollectMessage);
+ return superImpl.apply(code, shellUid, SHELL_PKG, featureId, virtualDeviceId,
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -382,8 +381,8 @@
final long identity = Binder.clearCallingIdentity();
try {
return superImpl.apply(code,
- new AttributionSource(shellUid, Process.INVALID_PID,
- "com.android.shell", attributionSource.getAttributionTag(),
+ new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG,
+ attributionSource.getAttributionTag(),
attributionSource.getToken(), /*renouncedPermissions*/ null,
attributionSource.getDeviceId(), attributionSource.getNext()),
shouldCollectAsyncNotedOp, message, shouldCollectMessage,
@@ -409,10 +408,9 @@
Process.SHELL_UID);
final long identity = Binder.clearCallingIdentity();
try {
- return superImpl.apply(token, code, shellUid, "com.android.shell",
- attributionTag, virtualDeviceId, startIfModeDefault,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage,
- attributionFlags, attributionChainId);
+ return superImpl.apply(token, code, shellUid, SHELL_PKG, attributionTag,
+ virtualDeviceId, startIfModeDefault, shouldCollectAsyncNotedOp, message,
+ shouldCollectMessage, attributionFlags, attributionChainId);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -438,8 +436,8 @@
final long identity = Binder.clearCallingIdentity();
try {
return superImpl.apply(clientId, code,
- new AttributionSource(shellUid, Process.INVALID_PID,
- "com.android.shell", attributionSource.getAttributionTag(),
+ new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG,
+ attributionSource.getAttributionTag(),
attributionSource.getToken(), /*renouncedPermissions*/ null,
attributionSource.getDeviceId(), attributionSource.getNext()),
startIfModeDefault, shouldCollectAsyncNotedOp, message,
@@ -465,11 +463,12 @@
final long identity = Binder.clearCallingIdentity();
try {
superImpl.apply(clientId, code,
- new AttributionSource(shellUid, Process.INVALID_PID,
- "com.android.shell", attributionSource.getAttributionTag(),
+ new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG,
+ attributionSource.getAttributionTag(),
attributionSource.getToken(), /*renouncedPermissions*/ null,
attributionSource.getDeviceId(), attributionSource.getNext()),
skipProxyOperation);
+ return;
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -477,6 +476,26 @@
superImpl.apply(clientId, code, attributionSource, skipProxyOperation);
}
+ @Override
+ public void finishOperation(IBinder clientId, int code, int uid, String packageName,
+ String attributionTag, int virtualDeviceId, @NonNull HexConsumer<IBinder, Integer,
+ Integer, String, String, Integer> superImpl) {
+ if (uid == mDelegateAndOwnerUid && isDelegateOp(code)) {
+ final int shellUid =
+ UserHandle.getUid(UserHandle.getUserId(uid), Process.SHELL_UID);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ superImpl.accept(clientId, code, shellUid, SHELL_PKG, attributionTag,
+ virtualDeviceId);
+ return;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ superImpl.accept(clientId, code, uid, packageName, attributionTag,
+ virtualDeviceId);
+ }
+
private boolean isDelegatePermission(@NonNull String permission) {
// null permissions means all permissions are delegated
return mDelegateAndOwnerUid != INVALID_UID
diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java
index 23872d4f..119b659 100644
--- a/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java
@@ -336,8 +336,13 @@
final PermissionManagerServiceInternal permissionManagerInternal =
LocalServices.getService(PermissionManagerServiceInternal.class);
for (final int userId : UserManagerService.getInstance().getUserIds()) {
- packageManagerInternal.forEachPackage(pkg ->
- permissionManagerInternal.resetRuntimePermissions(pkg, userId));
+ packageManagerInternal.forEachPackage(pkg -> {
+ // Filter out packages that don't have app IDs which means they don't have
+ // permission states either.
+ if (pkg.getUid() != -1) {
+ permissionManagerInternal.resetRuntimePermissions(pkg, userId);
+ }
+ });
}
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index cd1d799..ea71953 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -165,6 +165,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
@@ -368,18 +369,32 @@
return false;
}
+ int userId = UserHandle.getUserId(uid);
+
+ boolean isInSetup =
+ getSecureInt(Settings.Secure.USER_SETUP_COMPLETE, userId)
+ .map(setupState -> setupState == 0)
+ .orElse(false);
+ if (isInSetup) {
+ return true;
+ }
+
+ boolean isInDeferredSetup =
+ getSecureInt(Settings.Secure.USER_SETUP_PERSONALIZATION_STATE, userId)
+ .map(state ->
+ state == Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED)
+ .orElse(false);
+ return isInDeferredSetup;
+ }
+
+ private Optional<Integer> getSecureInt(String settingName, int userId) {
try {
- int userId = UserHandle.getUserId(uid);
- boolean isInSetup = Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.USER_SETUP_COMPLETE, userId) == 0;
- boolean isInDeferredSetup = Settings.Secure.getIntForUser(
- mContext.getContentResolver(),
- Settings.Secure.USER_SETUP_PERSONALIZATION_STATE, userId)
- == Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED;
- return isInSetup || isInDeferredSetup;
+ return Optional.of(
+ Settings.Secure.getIntForUser(
+ mContext.getContentResolver(), settingName, userId));
} catch (Settings.SettingNotFoundException e) {
- Slog.w(LOG_TAG, "Failed to check if the user is in restore: " + e);
- return false;
+ Slog.i(LOG_TAG, "Setting " + settingName + " not found", e);
+ return Optional.empty();
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 12e5180..4e224a3 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2882,6 +2882,8 @@
}
ContentResolver resolver = mContext.getContentResolver();
boolean updateRotation = false;
+ boolean updateKidsModeSettings = false;
+ final boolean kidsModeEnabled;
synchronized (mLock) {
mEndcallBehavior = Settings.System.getIntForUser(resolver,
Settings.System.END_BUTTON_BEHAVIOR,
@@ -2995,20 +2997,23 @@
Secure.STYLUS_BUTTONS_ENABLED, 1, UserHandle.USER_CURRENT) == 1;
mInputManagerInternal.setStylusButtonMotionEventsEnabled(mStylusButtonsEnabled);
- final boolean kidsModeEnabled = Settings.Secure.getIntForUser(resolver,
+ kidsModeEnabled = Settings.Secure.getIntForUser(resolver,
Settings.Secure.NAV_BAR_KIDS_MODE, 0, UserHandle.USER_CURRENT) == 1;
if (mKidsModeEnabled != kidsModeEnabled) {
mKidsModeEnabled = kidsModeEnabled;
- updateKidsModeSettings();
+ updateKidsModeSettings = true;
}
}
+ if (updateKidsModeSettings) {
+ updateKidsModeSettings(kidsModeEnabled);
+ }
if (updateRotation) {
updateRotation(true);
}
}
- private void updateKidsModeSettings() {
- if (mKidsModeEnabled) {
+ private void updateKidsModeSettings(boolean kidsModeEnabled) {
+ if (kidsModeEnabled) {
// Needed since many Kids apps aren't optimised to support both orientations and it
// will be hard for kids to understand the app compat mode.
// TODO(229961548): Remove ignoreOrientationRequest exception for Kids Mode once
diff --git a/services/core/java/com/android/server/policy/TalkbackShortcutController.java b/services/core/java/com/android/server/policy/TalkbackShortcutController.java
index b05a421..e544ae6 100644
--- a/services/core/java/com/android/server/policy/TalkbackShortcutController.java
+++ b/services/core/java/com/android/server/policy/TalkbackShortcutController.java
@@ -117,6 +117,7 @@
}
private boolean isTalkback(ServiceInfo info) {
- return TALKBACK_LABEL.equals(info.loadLabel(mPackageManager).toString());
+ return TALKBACK_LABEL.equals(info.loadLabel(mPackageManager).toString())
+ && (info.applicationInfo.isSystemApp() || info.applicationInfo.isUpdatedSystemApp());
}
}
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index df502eb..06595ac 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -19,6 +19,7 @@
import static android.os.Flags.adpfUseFmqChannel;
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
+import static com.android.server.power.hint.Flags.adpfSessionTag;
import static com.android.server.power.hint.Flags.powerhintThreadCleanup;
import android.annotation.NonNull;
@@ -28,6 +29,8 @@
import android.app.StatsManager;
import android.app.UidObserver;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.hardware.power.ChannelConfig;
import android.hardware.power.IPower;
import android.hardware.power.SessionConfig;
@@ -130,6 +133,7 @@
private final IPower mPowerHal;
private int mPowerHalVersion;
+ private final PackageManager mPackageManager;
private static final String PROPERTY_SF_ENABLE_CPU_HINT = "debug.sf.enable_adpf_cpu_hint";
private static final String PROPERTY_HWUI_ENABLE_HINT_MANAGER = "debug.hwui.use_hint_manager";
@@ -151,6 +155,11 @@
mCleanUpHandler = null;
mNonIsolatedTids = null;
}
+ if (adpfSessionTag()) {
+ mPackageManager = mContext.getPackageManager();
+ } else {
+ mPackageManager = null;
+ }
mActiveSessions = new ArrayMap<>();
mChannelMap = new ArrayMap<>();
mNativeWrapper = injector.createNativeWrapper();
@@ -819,6 +828,25 @@
throw new SecurityException(errMsg);
}
+ if (adpfSessionTag() && tag == SessionTag.APP) {
+ // If the category of the app is a game,
+ // we change the session tag to SessionTag.GAME
+ // as it was not previously classified
+ switch (getUidApplicationCategory(callingUid)) {
+ case ApplicationInfo.CATEGORY_GAME:
+ tag = SessionTag.GAME;
+ break;
+ case ApplicationInfo.CATEGORY_UNDEFINED:
+ // We use CATEGORY_UNDEFINED to filter the case when
+ // PackageManager.NameNotFoundException is caught,
+ // which should not happen.
+ tag = SessionTag.APP;
+ break;
+ default:
+ tag = SessionTag.APP;
+ }
+ }
+
Long halSessionPtr = null;
if (mConfigCreationSupport.get()) {
try {
@@ -857,7 +885,10 @@
}
}
- logPerformanceHintSessionAtom(callingUid, halSessionPtr, durationNanos, tids);
+ final long sessionId = config != null ? config.id : halSessionPtr;
+ logPerformanceHintSessionAtom(
+ callingUid, sessionId, durationNanos, tids, tag);
+
synchronized (mLock) {
AppHintSession hs = new AppHintSession(callingUid, callingTgid, tids, token,
halSessionPtr, durationNanos);
@@ -944,9 +975,20 @@
}
private void logPerformanceHintSessionAtom(int uid, long sessionId,
- long targetDuration, int[] tids) {
+ long targetDuration, int[] tids, @SessionTag int sessionTag) {
FrameworkStatsLog.write(FrameworkStatsLog.PERFORMANCE_HINT_SESSION_REPORTED, uid,
- sessionId, targetDuration, tids.length);
+ sessionId, targetDuration, tids.length, sessionTag);
+ }
+
+ private int getUidApplicationCategory(int uid) {
+ try {
+ final String packageName = mPackageManager.getNameForUid(uid);
+ final ApplicationInfo applicationInfo =
+ mPackageManager.getApplicationInfo(packageName, PackageManager.MATCH_ALL);
+ return applicationInfo.category;
+ } catch (PackageManager.NameNotFoundException e) {
+ return ApplicationInfo.CATEGORY_UNDEFINED;
+ }
}
}
diff --git a/services/core/java/com/android/server/power/hint/TEST_MAPPING b/services/core/java/com/android/server/power/hint/TEST_MAPPING
index ce6277d..34c25c6 100644
--- a/services/core/java/com/android/server/power/hint/TEST_MAPPING
+++ b/services/core/java/com/android/server/power/hint/TEST_MAPPING
@@ -10,16 +10,18 @@
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
- },
+ }
+ ],
+ "postsubmit": [
{
"name": "CtsStatsdAtomHostTestCases",
"options": [
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "org.junit.Ignore"},
- {"include-filter": "android.cts.statsdatom.powermanager"}
+ {"include-filter": "android.cts.statsdatom.performancehintmanager"}
],
"file_patterns": [
- "(/|^)ThermalManagerService.java"
+ "(/|^)HintManagerService.java"
]
}
]
diff --git a/services/core/java/com/android/server/power/hint/flags.aconfig b/services/core/java/com/android/server/power/hint/flags.aconfig
index 0997744..55afa05 100644
--- a/services/core/java/com/android/server/power/hint/flags.aconfig
+++ b/services/core/java/com/android/server/power/hint/flags.aconfig
@@ -7,3 +7,10 @@
description: "Feature flag for auto PowerHintSession dead thread cleanup"
bug: "296160319"
}
+
+flag {
+ name: "adpf_session_tag"
+ namespace: "game"
+ description: "Feature flag for adding session tag to hint session atom"
+ bug: "345011125"
+}
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 5bae5a4..322ed86 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -298,6 +298,8 @@
private final MobileRadioPowerStatsCollector mMobileRadioPowerStatsCollector;
private final WifiPowerStatsCollector mWifiPowerStatsCollector;
private final BluetoothPowerStatsCollector mBluetoothPowerStatsCollector;
+ private final CameraPowerStatsCollector mCameraPowerStatsCollector;
+ private final GnssPowerStatsCollector mGnssPowerStatsCollector;
private final SparseBooleanArray mPowerStatsCollectorEnabled = new SparseBooleanArray();
private final WifiPowerStatsCollector.WifiStatsRetriever mWifiStatsRetriever =
new WifiPowerStatsCollector.WifiStatsRetriever() {
@@ -1963,7 +1965,7 @@
private class PowerStatsCollectorInjector implements CpuPowerStatsCollector.Injector,
MobileRadioPowerStatsCollector.Injector, WifiPowerStatsCollector.Injector,
- BluetoothPowerStatsCollector.Injector {
+ BluetoothPowerStatsCollector.Injector, EnergyConsumerPowerStatsCollector.Injector {
private PackageManager mPackageManager;
private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
private NetworkStatsManager mNetworkStatsManager;
@@ -5446,7 +5448,10 @@
final int mappedUid = mapUid(uid);
if (mGpsNesting == 0) {
mHistory.recordStateStartEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE_GPS_ON_FLAG);
+ HistoryItem.STATE_GPS_ON_FLAG, uid, "gnss");
+ if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_GNSS)) {
+ mGnssPowerStatsCollector.schedule();
+ }
}
mGpsNesting++;
@@ -5465,11 +5470,14 @@
mGpsNesting--;
if (mGpsNesting == 0) {
mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE_GPS_ON_FLAG);
+ HistoryItem.STATE_GPS_ON_FLAG, uid, "gnss");
mHistory.recordGpsSignalQualityEvent(elapsedRealtimeMs, uptimeMs,
GPS_SIGNAL_QUALITY_NONE);
stopAllGpsSignalQualityTimersLocked(-1, elapsedRealtimeMs);
mGpsSignalQualityBin = -1;
+ if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_GNSS)) {
+ mGnssPowerStatsCollector.schedule();
+ }
}
mFrameworkStatsLogger.gpsScanStateChanged(mapIsolatedUid(uid), workChain, /* on */ false);
@@ -6652,13 +6660,17 @@
uid = mapUid(uid);
if (mCameraOnNesting++ == 0) {
mHistory.recordState2StartEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE2_CAMERA_FLAG);
+ HistoryItem.STATE2_CAMERA_FLAG, uid, "camera");
mCameraOnTimer.startRunningLocked(elapsedRealtimeMs);
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteCameraTurnedOnLocked(elapsedRealtimeMs);
- scheduleSyncExternalStatsLocked("camera-on", ExternalStatsSync.UPDATE_CAMERA);
+ if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CAMERA)) {
+ mCameraPowerStatsCollector.schedule();
+ } else {
+ scheduleSyncExternalStatsLocked("camera-on", ExternalStatsSync.UPDATE_CAMERA);
+ }
}
@GuardedBy("this")
@@ -6669,13 +6681,17 @@
uid = mapUid(uid);
if (--mCameraOnNesting == 0) {
mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE2_CAMERA_FLAG);
+ HistoryItem.STATE2_CAMERA_FLAG, uid, "camera");
mCameraOnTimer.stopRunningLocked(elapsedRealtimeMs);
}
getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
.noteCameraTurnedOffLocked(elapsedRealtimeMs);
- scheduleSyncExternalStatsLocked("camera-off", ExternalStatsSync.UPDATE_CAMERA);
+ if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CAMERA)) {
+ mCameraPowerStatsCollector.schedule();
+ } else {
+ scheduleSyncExternalStatsLocked("camera-off", ExternalStatsSync.UPDATE_CAMERA);
+ }
}
@GuardedBy("this")
@@ -11281,6 +11297,12 @@
mPowerStatsCollectorInjector);
mBluetoothPowerStatsCollector.addConsumer(this::recordPowerStats);
+ mCameraPowerStatsCollector = new CameraPowerStatsCollector(mPowerStatsCollectorInjector);
+ mCameraPowerStatsCollector.addConsumer(this::recordPowerStats);
+
+ mGnssPowerStatsCollector = new GnssPowerStatsCollector(mPowerStatsCollectorInjector);
+ mGnssPowerStatsCollector.addConsumer(this::recordPowerStats);
+
mStartCount++;
initTimersAndCounters();
mOnBattery = mOnBatteryInternal = false;
@@ -14703,6 +14725,14 @@
mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_BLUETOOTH));
mBluetoothPowerStatsCollector.schedule();
+ mCameraPowerStatsCollector.setEnabled(
+ mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CAMERA));
+ mCameraPowerStatsCollector.schedule();
+
+ mGnssPowerStatsCollector.setEnabled(
+ mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_GNSS));
+ mGnssPowerStatsCollector.schedule();
+
mSystemReady = true;
}
@@ -14721,6 +14751,10 @@
return mWifiPowerStatsCollector;
case BatteryConsumer.POWER_COMPONENT_BLUETOOTH:
return mBluetoothPowerStatsCollector;
+ case BatteryConsumer.POWER_COMPONENT_CAMERA:
+ return mCameraPowerStatsCollector;
+ case BatteryConsumer.POWER_COMPONENT_GNSS:
+ return mGnssPowerStatsCollector;
}
return null;
}
@@ -16258,6 +16292,8 @@
mMobileRadioPowerStatsCollector.forceSchedule();
mWifiPowerStatsCollector.forceSchedule();
mBluetoothPowerStatsCollector.forceSchedule();
+ mCameraPowerStatsCollector.forceSchedule();
+ mGnssPowerStatsCollector.forceSchedule();
}
/**
@@ -16278,6 +16314,8 @@
mMobileRadioPowerStatsCollector.collectAndDump(pw);
mWifiPowerStatsCollector.collectAndDump(pw);
mBluetoothPowerStatsCollector.collectAndDump(pw);
+ mCameraPowerStatsCollector.collectAndDump(pw);
+ mGnssPowerStatsCollector.collectAndDump(pw);
}
private final Runnable mWriteAsyncRunnable = () -> {
diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
index ba6e4a9..ce0ee39 100644
--- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
+++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
@@ -95,8 +95,12 @@
}
mPowerCalculators.add(new SensorPowerCalculator(
mContext.getSystemService(SensorManager.class)));
- mPowerCalculators.add(new GnssPowerCalculator(mPowerProfile));
- mPowerCalculators.add(new CameraPowerCalculator(mPowerProfile));
+ if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_GNSS)) {
+ mPowerCalculators.add(new GnssPowerCalculator(mPowerProfile));
+ }
+ if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_CAMERA)) {
+ mPowerCalculators.add(new CameraPowerCalculator(mPowerProfile));
+ }
if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) {
mPowerCalculators.add(new FlashlightPowerCalculator(mPowerProfile));
}
diff --git a/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsProcessor.java
index 490bd5e..599e63d 100644
--- a/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsProcessor.java
@@ -51,7 +51,7 @@
private long mLastUpdateTimestamp;
private PowerStats.Descriptor mDescriptor;
- private final BinaryStatePowerStatsLayout mStatsLayout = new BinaryStatePowerStatsLayout();
+ private final BinaryStatePowerStatsLayout mStatsLayout;
private PowerStats mPowerStats;
private PowerEstimationPlan mPlan;
private long[] mTmpDeviceStatsArray;
@@ -59,9 +59,17 @@
BinaryStatePowerStatsProcessor(int powerComponentId,
PowerStatsUidResolver uidResolver, double averagePowerMilliAmp) {
+ this(powerComponentId, uidResolver, averagePowerMilliAmp,
+ new BinaryStatePowerStatsLayout());
+ }
+
+ BinaryStatePowerStatsProcessor(int powerComponentId,
+ PowerStatsUidResolver uidResolver, double averagePowerMilliAmp,
+ BinaryStatePowerStatsLayout statsLayout) {
mPowerComponentId = powerComponentId;
mUsageBasedPowerEstimator = new UsageBasedPowerEstimator(averagePowerMilliAmp);
mUidResolver = uidResolver;
+ mStatsLayout = statsLayout;
}
protected abstract @BinaryState int getBinaryState(BatteryStats.HistoryItem item);
@@ -107,7 +115,7 @@
mInitiatingUid = mUidResolver.mapUid(item.eventTag.uid);
}
} else {
- recordUsageDuration(item.time);
+ recordUsageDuration(mPowerStats, mInitiatingUid, item.time);
mInitiatingUid = Process.INVALID_UID;
if (!mEnergyConsumerSupported) {
flushPowerStats(stats, item.time);
@@ -117,20 +125,16 @@
mLastState = state;
}
- private void recordUsageDuration(long time) {
- if (mLastState == STATE_OFF) {
- return;
- }
-
+ protected void recordUsageDuration(PowerStats powerStats, int uid, long time) {
long durationMs = time - mLastStateTimestamp;
mStatsLayout.setUsageDuration(mPowerStats.stats,
mStatsLayout.getUsageDuration(mPowerStats.stats) + durationMs);
- if (mInitiatingUid != Process.INVALID_UID) {
- long[] uidStats = mPowerStats.uidStats.get(mInitiatingUid);
+ if (uid != Process.INVALID_UID) {
+ long[] uidStats = mPowerStats.uidStats.get(uid);
if (uidStats == null) {
uidStats = new long[mDescriptor.uidStatsArrayLength];
- mPowerStats.uidStats.put(mInitiatingUid, uidStats);
+ mPowerStats.uidStats.put(uid, uidStats);
mStatsLayout.setUidUsageDuration(uidStats, durationMs);
} else {
mStatsLayout.setUsageDuration(mPowerStats.stats,
@@ -143,7 +147,11 @@
void addPowerStats(PowerComponentAggregatedPowerStats stats, PowerStats powerStats,
long timestampMs) {
ensureInitialized();
- recordUsageDuration(timestampMs);
+
+ if (mLastState == STATE_ON) {
+ recordUsageDuration(mPowerStats, mInitiatingUid, timestampMs);
+ }
+
long consumedEnergy = mStatsLayout.getConsumedEnergy(powerStats.stats, 0);
if (consumedEnergy != BatteryStats.POWER_DATA_UNAVAILABLE) {
mEnergyConsumerSupported = true;
@@ -169,14 +177,16 @@
@Override
void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
- recordUsageDuration(timestampMs);
+ if (mLastState == STATE_ON) {
+ recordUsageDuration(mPowerStats, mInitiatingUid, timestampMs);
+ }
flushPowerStats(stats, timestampMs);
if (mPlan == null) {
mPlan = new PowerEstimationPlan(stats.getConfig());
}
- computeDevicePowerEstimates(stats);
+ computeDevicePowerEstimates(stats, mPlan, mEnergyConsumerSupported);
combineDevicePowerEstimates(stats);
List<Integer> uids = new ArrayList<>();
@@ -186,9 +196,10 @@
computeUidPowerEstimates(stats, uids);
}
- private void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats) {
- for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
- DeviceStateEstimation estimation = mPlan.deviceStateEstimations.get(i);
+ protected void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats,
+ PowerEstimationPlan plan, boolean energyConsumerSupported) {
+ for (int i = plan.deviceStateEstimations.size() - 1; i >= 0; i--) {
+ DeviceStateEstimation estimation = plan.deviceStateEstimations.get(i);
if (!stats.getDeviceStats(mTmpDeviceStatsArray, estimation.stateValues)) {
continue;
}
@@ -196,7 +207,7 @@
long duration = mStatsLayout.getUsageDuration(mTmpDeviceStatsArray);
if (duration > 0) {
double power;
- if (mEnergyConsumerSupported) {
+ if (energyConsumerSupported) {
power = uCtoMah(mStatsLayout.getConsumedEnergy(mTmpDeviceStatsArray, 0));
} else {
power = mUsageBasedPowerEstimator.calculatePower(duration);
diff --git a/services/core/java/com/android/server/power/stats/CameraPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CameraPowerStatsCollector.java
new file mode 100644
index 0000000..8705bd5
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/CameraPowerStatsCollector.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import android.hardware.power.stats.EnergyConsumerType;
+import android.os.BatteryConsumer;
+
+public class CameraPowerStatsCollector extends EnergyConsumerPowerStatsCollector {
+
+ CameraPowerStatsCollector(Injector injector) {
+ super(injector, BatteryConsumer.POWER_COMPONENT_CAMERA,
+ BatteryConsumer.powerComponentIdToString(BatteryConsumer.POWER_COMPONENT_CAMERA),
+ EnergyConsumerType.CAMERA, /* energy consumer name */ null,
+ new BinaryStatePowerStatsLayout());
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/CameraPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/CameraPowerStatsProcessor.java
new file mode 100644
index 0000000..15c3eb8
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/CameraPowerStatsProcessor.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+
+import com.android.internal.os.PowerProfile;
+
+public class CameraPowerStatsProcessor extends BinaryStatePowerStatsProcessor {
+ public CameraPowerStatsProcessor(PowerProfile powerProfile,
+ PowerStatsUidResolver uidResolver) {
+ super(BatteryConsumer.POWER_COMPONENT_CAMERA, uidResolver,
+ powerProfile.getAveragePower(PowerProfile.POWER_CAMERA));
+ }
+
+ @Override
+ protected @BinaryState int getBinaryState(BatteryStats.HistoryItem item) {
+ return (item.states2 & BatteryStats.HistoryItem.STATE2_CAMERA_FLAG) != 0
+ ? STATE_ON
+ : STATE_OFF;
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsCollector.java
new file mode 100644
index 0000000..2021f85
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsCollector.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import android.hardware.power.stats.EnergyConsumerType;
+import android.os.Handler;
+import android.os.PersistableBundle;
+import android.util.Slog;
+
+import com.android.internal.os.Clock;
+import com.android.internal.os.PowerStats;
+
+import java.util.function.IntSupplier;
+
+public class EnergyConsumerPowerStatsCollector extends PowerStatsCollector {
+ private static final String TAG = "CameraPowerStatsCollector";
+
+ private static final long CAMERA_ACTIVITY_REQUEST_TIMEOUT = 20000;
+
+ private static final long ENERGY_UNSPECIFIED = -1;
+
+ interface Injector {
+ Handler getHandler();
+ Clock getClock();
+ PowerStatsUidResolver getUidResolver();
+ long getPowerStatsCollectionThrottlePeriod(String powerComponentName);
+ ConsumedEnergyRetriever getConsumedEnergyRetriever();
+ IntSupplier getVoltageSupplier();
+ }
+
+ private final Injector mInjector;
+ private final int mPowerComponentId;
+ private final String mPowerComponentName;
+ private final int mEnergyConsumerType;
+ private final String mEnergyConsumerName;
+
+ private final BinaryStatePowerStatsLayout mLayout;
+ private boolean mIsInitialized;
+
+ private PowerStats mPowerStats;
+ private ConsumedEnergyRetriever mConsumedEnergyRetriever;
+ private IntSupplier mVoltageSupplier;
+ private int[] mEnergyConsumerIds = new int[0];
+ private long mLastConsumedEnergyUws = ENERGY_UNSPECIFIED;
+ private int mLastVoltageMv;
+ private long mLastUpdateTimestamp;
+ private boolean mFirstCollection = true;
+
+ EnergyConsumerPowerStatsCollector(Injector injector, int powerComponentId,
+ String powerComponentName, @EnergyConsumerType int energyConsumerType,
+ String energyConsumerName, BinaryStatePowerStatsLayout statsLayout) {
+ super(injector.getHandler(),
+ injector.getPowerStatsCollectionThrottlePeriod(powerComponentName),
+ injector.getUidResolver(), injector.getClock());
+ mInjector = injector;
+ mPowerComponentId = powerComponentId;
+ mPowerComponentName = powerComponentName;
+ mEnergyConsumerType = energyConsumerType;
+ mEnergyConsumerName = energyConsumerName;
+ mLayout = statsLayout;
+ }
+
+ private boolean ensureInitialized() {
+ if (mIsInitialized) {
+ return true;
+ }
+
+ if (!isEnabled()) {
+ return false;
+ }
+
+ mConsumedEnergyRetriever = mInjector.getConsumedEnergyRetriever();
+ mVoltageSupplier = mInjector.getVoltageSupplier();
+ mEnergyConsumerIds = mConsumedEnergyRetriever.getEnergyConsumerIds(mEnergyConsumerType,
+ mEnergyConsumerName);
+
+ PersistableBundle extras = new PersistableBundle();
+ mLayout.toExtras(extras);
+ PowerStats.Descriptor powerStatsDescriptor = new PowerStats.Descriptor(
+ mPowerComponentId, mPowerComponentName, mLayout.getDeviceStatsArrayLength(),
+ null, 0, mLayout.getUidStatsArrayLength(),
+ extras);
+ mPowerStats = new PowerStats(powerStatsDescriptor);
+
+ mIsInitialized = true;
+ return true;
+ }
+
+ @Override
+ protected PowerStats collectStats() {
+ if (!ensureInitialized()) {
+ return null;
+ }
+
+ if (mEnergyConsumerIds.length == 0) {
+ return null;
+ }
+
+ long consumedEnergy = 0;
+ int voltageMv = mVoltageSupplier.getAsInt();
+ if (voltageMv <= 0) {
+ Slog.wtf(TAG, "Unexpected battery voltage (" + voltageMv
+ + " mV) when querying energy consumers");
+ } else {
+ long[] energyUws = mConsumedEnergyRetriever.getConsumedEnergyUws(mEnergyConsumerIds);
+ if (energyUws != null) {
+ for (int i = energyUws.length - 1; i >= 0; i--) {
+ if (energyUws[i] != ENERGY_UNSPECIFIED) {
+ consumedEnergy += energyUws[i];
+ }
+ }
+ }
+ }
+
+ long energyDelta = mLastConsumedEnergyUws != ENERGY_UNSPECIFIED
+ ? consumedEnergy - mLastConsumedEnergyUws : 0;
+ mLastConsumedEnergyUws = consumedEnergy;
+ if (energyDelta < 0) {
+ // Likely, restart of powerstats HAL
+ energyDelta = 0;
+ }
+
+ if (energyDelta == 0 && !mFirstCollection) {
+ return null;
+ }
+
+ int averageVoltage = mLastVoltageMv != 0 ? (mLastVoltageMv + voltageMv) / 2 : voltageMv;
+ mLastVoltageMv = voltageMv;
+ mLayout.setConsumedEnergy(mPowerStats.stats, 0, uJtoUc(energyDelta, averageVoltage));
+ long timestamp = mClock.elapsedRealtime();
+ mPowerStats.durationMs = timestamp - mLastUpdateTimestamp;
+ mLastUpdateTimestamp = timestamp;
+ mFirstCollection = false;
+ return mPowerStats;
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/GnssPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/GnssPowerStatsCollector.java
new file mode 100644
index 0000000..168a874
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/GnssPowerStatsCollector.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import android.hardware.power.stats.EnergyConsumerType;
+import android.os.BatteryConsumer;
+
+public class GnssPowerStatsCollector extends EnergyConsumerPowerStatsCollector {
+
+ GnssPowerStatsCollector(Injector injector) {
+ super(injector, BatteryConsumer.POWER_COMPONENT_GNSS,
+ BatteryConsumer.powerComponentIdToString(BatteryConsumer.POWER_COMPONENT_GNSS),
+ EnergyConsumerType.GNSS, /* energy consumer name */ null,
+ new GnssPowerStatsLayout());
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/GnssPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/GnssPowerStatsLayout.java
new file mode 100644
index 0000000..9a1317d
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/GnssPowerStatsLayout.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import android.location.GnssSignalQuality;
+import android.os.PersistableBundle;
+
+class GnssPowerStatsLayout extends BinaryStatePowerStatsLayout {
+ private static final String EXTRA_DEVICE_TIME_SIGNAL_LEVEL_POSITION = "dt-sig";
+ private static final String EXTRA_UID_TIME_SIGNAL_LEVEL_POSITION = "ut-sig";
+
+ private int mDeviceSignalLevelTimePosition;
+ private int mUidSignalLevelTimePosition;
+
+ GnssPowerStatsLayout() {
+ mDeviceSignalLevelTimePosition = addDeviceSection(
+ GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS, "level");
+ mUidSignalLevelTimePosition = addUidSection(
+ GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS, "level");
+ }
+
+ @Override
+ public void fromExtras(PersistableBundle extras) {
+ super.fromExtras(extras);
+ mDeviceSignalLevelTimePosition = extras.getInt(EXTRA_DEVICE_TIME_SIGNAL_LEVEL_POSITION);
+ mUidSignalLevelTimePosition = extras.getInt(EXTRA_UID_TIME_SIGNAL_LEVEL_POSITION);
+ }
+
+ @Override
+ public void toExtras(PersistableBundle extras) {
+ super.toExtras(extras);
+ extras.putInt(EXTRA_DEVICE_TIME_SIGNAL_LEVEL_POSITION, mDeviceSignalLevelTimePosition);
+ extras.putInt(EXTRA_UID_TIME_SIGNAL_LEVEL_POSITION, mUidSignalLevelTimePosition);
+ }
+
+ public void setDeviceSignalLevelTime(long[] stats, int signalLevel, long durationMillis) {
+ stats[mDeviceSignalLevelTimePosition + signalLevel] = durationMillis;
+ }
+
+ public long getDeviceSignalLevelTime(long[] stats, int signalLevel) {
+ return stats[mDeviceSignalLevelTimePosition + signalLevel];
+ }
+
+ public void setUidSignalLevelTime(long[] stats, int signalLevel, long durationMillis) {
+ stats[mUidSignalLevelTimePosition + signalLevel] = durationMillis;
+ }
+
+ public long getUidSignalLevelTime(long[] stats, int signalLevel) {
+ return stats[mUidSignalLevelTimePosition + signalLevel];
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/GnssPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/GnssPowerStatsProcessor.java
new file mode 100644
index 0000000..572bde9
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/GnssPowerStatsProcessor.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import android.location.GnssSignalQuality;
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.Process;
+
+import com.android.internal.os.PowerProfile;
+import com.android.internal.os.PowerStats;
+
+import java.util.Arrays;
+
+public class GnssPowerStatsProcessor extends BinaryStatePowerStatsProcessor {
+ private int mGnssSignalLevel = GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN;
+ private long mGnssSignalLevelTimestamp;
+ private final long[] mGnssSignalDurations =
+ new long[GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS];
+ private static final GnssPowerStatsLayout sStatsLayout = new GnssPowerStatsLayout();
+ private final UsageBasedPowerEstimator[] mSignalLevelEstimators =
+ new UsageBasedPowerEstimator[GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS];
+ private final boolean mUseSignalLevelEstimators;
+ private long[] mTmpDeviceStatsArray;
+
+ public GnssPowerStatsProcessor(PowerProfile powerProfile, PowerStatsUidResolver uidResolver) {
+ super(BatteryConsumer.POWER_COMPONENT_GNSS, uidResolver,
+ powerProfile.getAveragePower(PowerProfile.POWER_GPS_ON),
+ sStatsLayout);
+
+ boolean useSignalLevelEstimators = false;
+ for (int level = 0; level < GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS; level++) {
+ double power = powerProfile.getAveragePower(
+ PowerProfile.POWER_GPS_SIGNAL_QUALITY_BASED, level);
+ if (power != 0) {
+ useSignalLevelEstimators = true;
+ }
+ mSignalLevelEstimators[level] = new UsageBasedPowerEstimator(power);
+ }
+ mUseSignalLevelEstimators = useSignalLevelEstimators;
+ }
+
+ @Override
+ protected @BinaryState int getBinaryState(BatteryStats.HistoryItem item) {
+ if ((item.states & BatteryStats.HistoryItem.STATE_GPS_ON_FLAG) == 0) {
+ mGnssSignalLevel = GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN;
+ return STATE_OFF;
+ }
+
+ noteGnssSignalLevel(item);
+ return STATE_ON;
+ }
+
+ private void noteGnssSignalLevel(BatteryStats.HistoryItem item) {
+ int signalLevel = (item.states2 & BatteryStats.HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK)
+ >> BatteryStats.HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT;
+ if (signalLevel >= GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS) {
+ signalLevel = GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN;
+ }
+ if (signalLevel == mGnssSignalLevel) {
+ return;
+ }
+
+ if (mGnssSignalLevel != GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN) {
+ mGnssSignalDurations[mGnssSignalLevel] += item.time - mGnssSignalLevelTimestamp;
+ }
+ mGnssSignalLevel = signalLevel;
+ mGnssSignalLevelTimestamp = item.time;
+ }
+
+ @Override
+ protected void recordUsageDuration(PowerStats powerStats, int uid, long time) {
+ super.recordUsageDuration(powerStats, uid, time);
+
+ if (mGnssSignalLevel != GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN) {
+ mGnssSignalDurations[mGnssSignalLevel] += time - mGnssSignalLevelTimestamp;
+ } else if (mUseSignalLevelEstimators) {
+ // Default GNSS signal quality to GOOD for the purposes of power attribution
+ mGnssSignalDurations[GnssSignalQuality.GNSS_SIGNAL_QUALITY_GOOD] +=
+ time - mGnssSignalLevelTimestamp;
+ }
+
+ for (int level = 0; level < GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS; level++) {
+ long duration = mGnssSignalDurations[level];
+ sStatsLayout.setDeviceSignalLevelTime(powerStats.stats, level, duration);
+ if (uid != Process.INVALID_UID) {
+ long[] uidStats = powerStats.uidStats.get(uid);
+ if (uidStats == null) {
+ uidStats = new long[powerStats.descriptor.uidStatsArrayLength];
+ powerStats.uidStats.put(uid, uidStats);
+ sStatsLayout.setUidSignalLevelTime(uidStats, level, duration);
+ } else {
+ sStatsLayout.setUidSignalLevelTime(uidStats, level,
+ sStatsLayout.getUidSignalLevelTime(uidStats, level) + duration);
+ }
+ }
+ }
+
+ mGnssSignalLevelTimestamp = time;
+ Arrays.fill(mGnssSignalDurations, 0);
+ }
+
+ protected void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats,
+ PowerEstimationPlan plan, boolean energyConsumerSupported) {
+ if (!mUseSignalLevelEstimators || energyConsumerSupported) {
+ super.computeDevicePowerEstimates(stats, plan, energyConsumerSupported);
+ return;
+ }
+
+ if (mTmpDeviceStatsArray == null) {
+ mTmpDeviceStatsArray = new long[stats.getPowerStatsDescriptor().statsArrayLength];
+ }
+
+ for (int i = plan.deviceStateEstimations.size() - 1; i >= 0; i--) {
+ DeviceStateEstimation estimation = plan.deviceStateEstimations.get(i);
+ if (!stats.getDeviceStats(mTmpDeviceStatsArray, estimation.stateValues)) {
+ continue;
+ }
+
+ double power = 0;
+ for (int level = 0; level < GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS; level++) {
+ long duration = sStatsLayout.getDeviceSignalLevelTime(mTmpDeviceStatsArray, level);
+ power += mSignalLevelEstimators[level].calculatePower(duration);
+ }
+ sStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray, power);
+ stats.setDeviceStats(estimation.stateValues, mTmpDeviceStatsArray);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
index b82c021..d442c61 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
@@ -231,10 +231,14 @@
}
interface ConsumedEnergyRetriever {
- int[] getEnergyConsumerIds(@EnergyConsumerType int energyConsumerType);
+ int[] getEnergyConsumerIds(@EnergyConsumerType int energyConsumerType, String name);
@Nullable
long[] getConsumedEnergyUws(int[] energyConsumerIds);
+
+ default int[] getEnergyConsumerIds(@EnergyConsumerType int energyConsumerType) {
+ return getEnergyConsumerIds(energyConsumerType, null);
+ }
}
static class ConsumedEnergyRetrieverImpl implements ConsumedEnergyRetriever {
@@ -245,7 +249,7 @@
}
@Override
- public int[] getEnergyConsumerIds(int energyConsumerType) {
+ public int[] getEnergyConsumerIds(int energyConsumerType, String name) {
if (mPowerStatsInternal == null) {
return new int[0];
}
@@ -257,7 +261,8 @@
List<EnergyConsumer> energyConsumers = new ArrayList<>();
for (EnergyConsumer energyConsumer : energyConsumerInfo) {
- if (energyConsumer.type == energyConsumerType) {
+ if (energyConsumer.type == energyConsumerType
+ && (name == null || name.equals(energyConsumer.name))) {
energyConsumers.add(energyConsumer);
}
}
diff --git a/services/core/java/com/android/server/powerstats/Android.bp b/services/core/java/com/android/server/powerstats/Android.bp
new file mode 100644
index 0000000..7f3b091
--- /dev/null
+++ b/services/core/java/com/android/server/powerstats/Android.bp
@@ -0,0 +1,11 @@
+aconfig_declarations {
+ name: "powerstats_flags",
+ package: "com.android.server.powerstats",
+ container: "system",
+ srcs: ["*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "powerstats_flags_lib",
+ aconfig_declarations: "powerstats_flags",
+}
diff --git a/services/core/java/com/android/server/powerstats/TimerTrigger.java b/services/core/java/com/android/server/powerstats/TimerTrigger.java
index f8a4135..817a40d 100644
--- a/services/core/java/com/android/server/powerstats/TimerTrigger.java
+++ b/services/core/java/com/android/server/powerstats/TimerTrigger.java
@@ -16,8 +16,10 @@
package com.android.server.powerstats;
+import android.app.AlarmManager;
import android.content.Context;
import android.os.Handler;
+import android.os.SystemClock;
import android.util.Slog;
/**
@@ -33,37 +35,53 @@
private static final long LOG_PERIOD_MS_HIGH_FREQUENCY = 2 * 60 * 1000; // 2 minutes
private final Handler mHandler;
+ private final AlarmManager mAlarmManager;
- private Runnable mLogDataLowFrequency = new Runnable() {
+ class PeriodicTimer implements Runnable, AlarmManager.OnAlarmListener {
+ private final String mName;
+ private final long mPeriodMs;
+ private final int mMsgType;
+
+ PeriodicTimer(String name, long periodMs, int msgType) {
+ mName = name;
+ mPeriodMs = periodMs;
+ mMsgType = msgType;
+ }
+
+ @Override
+ public void onAlarm() {
+ run();
+ }
+
@Override
public void run() {
- // Do not wake the device for these messages. Opportunistically log rail data every
- // LOG_PERIOD_MS_LOW_FREQUENCY.
- mHandler.postDelayed(mLogDataLowFrequency, LOG_PERIOD_MS_LOW_FREQUENCY);
- if (DEBUG) Slog.d(TAG, "Received delayed message. Log rail data low frequency");
- logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_LOW_FREQUENCY);
+ if (Flags.alarmBasedPowerstatsLogging()) {
+ final long nextAlarmMs = SystemClock.elapsedRealtime() + mPeriodMs;
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, nextAlarmMs,
+ AlarmManager.WINDOW_EXACT, 0, mName, this, mHandler, null);
+ } else {
+ mHandler.postDelayed(this, mPeriodMs);
+ }
+ if (DEBUG) Slog.d(TAG, "Received delayed message (" + mName + "). Logging rail data");
+ logPowerStatsData(mMsgType);
}
- };
-
- private Runnable mLogDataHighFrequency = new Runnable() {
- @Override
- public void run() {
- // Do not wake the device for these messages. Opportunistically log rail data every
- // LOG_PERIOD_MS_HIGH_FREQUENCY.
- mHandler.postDelayed(mLogDataHighFrequency, LOG_PERIOD_MS_HIGH_FREQUENCY);
- if (DEBUG) Slog.d(TAG, "Received delayed message. Log rail data high frequency");
- logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_HIGH_FREQUENCY);
- }
- };
+ }
public TimerTrigger(Context context, PowerStatsLogger powerStatsLogger,
boolean triggerEnabled) {
super(context, powerStatsLogger);
mHandler = mContext.getMainThreadHandler();
+ mAlarmManager = mContext.getSystemService(AlarmManager.class);
if (triggerEnabled) {
- mLogDataLowFrequency.run();
- mLogDataHighFrequency.run();
+ final PeriodicTimer logDataLowFrequency = new PeriodicTimer("PowerStatsLowFreqLog",
+ LOG_PERIOD_MS_LOW_FREQUENCY,
+ PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_LOW_FREQUENCY);
+ final PeriodicTimer logDataHighFrequency = new PeriodicTimer("PowerStatsHighFreqLog",
+ LOG_PERIOD_MS_HIGH_FREQUENCY,
+ PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_HIGH_FREQUENCY);
+ logDataLowFrequency.run();
+ logDataHighFrequency.run();
}
}
}
diff --git a/services/core/java/com/android/server/powerstats/flags.aconfig b/services/core/java/com/android/server/powerstats/flags.aconfig
new file mode 100644
index 0000000..0a4a751
--- /dev/null
+++ b/services/core/java/com/android/server/powerstats/flags.aconfig
@@ -0,0 +1,13 @@
+
+package: "com.android.server.powerstats"
+container: "system"
+
+flag {
+ name: "alarm_based_powerstats_logging"
+ namespace: "backstage_power"
+ description: "Utilize new OomAdjuster implementation"
+ bug: "294598168"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 04db3e8..3138a9e 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -1870,6 +1870,11 @@
@Override
public boolean isInSignificantPlace() {
+ if (android.security.Flags.significantPlaces()) {
+ mSignificantPlaceServiceWatcher.runOnBinder(
+ binder -> ISignificantPlaceProvider.Stub.asInterface(binder)
+ .onSignificantPlaceCheck());
+ }
return mIsInSignificantPlace;
}
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
old mode 100755
new mode 100644
diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java
index 6710d02..988e8fe 100644
--- a/services/core/java/com/android/server/vibrator/VibratorController.java
+++ b/services/core/java/com/android/server/vibrator/VibratorController.java
@@ -56,10 +56,17 @@
private volatile boolean mIsUnderExternalControl;
private volatile float mCurrentAmplitude;
- /** Listener for vibration completion callbacks from native. */
+ /**
+ * Listener for vibration completion callbacks from native.
+ *
+ * <p>Only the latest active native call to {@link VibratorController#on} will ever trigger this
+ * completion callback, to avoid race conditions during a vibration playback. If a new call to
+ * {@link #on} or {@link #off} happens before a previous callback was triggered then the
+ * previous callback will be disabled, even if the new command fails.
+ */
public interface OnVibrationCompleteListener {
- /** Callback triggered when vibration is complete. */
+ /** Callback triggered when an active vibration command is complete. */
void onComplete(int vibratorId, long vibrationId);
}
@@ -235,7 +242,7 @@
}
/**
- * Turn on the vibrator for {@code milliseconds} time, using {@code vibrationId} or completion
+ * Turn on the vibrator for {@code milliseconds} time, using {@code vibrationId} for completion
* callback to {@link OnVibrationCompleteListener}.
*
* <p>This will affect the state of {@link #isVibrating()}.
@@ -255,7 +262,7 @@
}
/**
- * Plays predefined vibration effect, using {@code vibrationId} or completion callback to
+ * Plays predefined vibration effect, using {@code vibrationId} for completion callback to
* {@link OnVibrationCompleteListener}.
*
* <p>This will affect the state of {@link #isVibrating()}.
@@ -276,8 +283,8 @@
}
/**
- * Plays a composition of vibration primitives, using {@code vibrationId} or completion callback
- * to {@link OnVibrationCompleteListener}.
+ * Plays a composition of vibration primitives, using {@code vibrationId} for completion
+ * callback to {@link OnVibrationCompleteListener}.
*
* <p>This will affect the state of {@link #isVibrating()}.
*
@@ -299,7 +306,7 @@
}
/**
- * Plays a composition of pwle primitives, using {@code vibrationId} or completion callback
+ * Plays a composition of pwle primitives, using {@code vibrationId} for completion callback
* to {@link OnVibrationCompleteListener}.
*
* <p>This will affect the state of {@link #isVibrating()}.
@@ -321,7 +328,11 @@
}
}
- /** Turns off the vibrator. This will affect the state of {@link #isVibrating()}. */
+ /**
+ * Turns off the vibrator and disables completion callback to any pending vibration.
+ *
+ * <p>This will affect the state of {@link #isVibrating()}.
+ */
public void off() {
synchronized (mLock) {
mNativeWrapper.off();
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index c9395da..3e177c9 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -1299,9 +1299,11 @@
// The restore windowing mode must be set after the windowing mode is set since
// Task#setWindowingMode resets the restore windowing mode to WINDOWING_MODE_INVALID.
requester.mMultiWindowRestoreWindowingMode = restoreWindowingMode;
+ requester.mMultiWindowRestoreParent =
+ requester.getParent().mRemoteToken.toWindowContainerToken();
} else {
targetWindowingMode = requester.mMultiWindowRestoreWindowingMode;
- requester.setWindowingMode(targetWindowingMode);
+ requester.restoreWindowingMode();
}
if (targetWindowingMode == WINDOWING_MODE_FULLSCREEN) {
requester.setBounds(null);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index fec1af4..5159fc4 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -156,6 +156,7 @@
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE;
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED;
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.ActivityRecord.State.DESTROYED;
@@ -661,7 +662,8 @@
*/
private CompatDisplayInsets mCompatDisplayInsets;
- private final TaskFragment.ConfigOverrideHint mResolveConfigHint;
+ @VisibleForTesting
+ final TaskFragment.ConfigOverrideHint mResolveConfigHint;
private final boolean mOptOutEdgeToEdge;
@@ -2981,7 +2983,7 @@
void removeStartingWindowAnimation(boolean prepareAnimation) {
mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_IDLE;
- if (task != null) {
+ if (mStartingData != null && task != null) {
task.mSharedStartingData = null;
}
if (mStartingWindow == null) {
@@ -4032,6 +4034,10 @@
if (next == null) {
mRootWindowContainer.ensureVisibilityAndConfig(null /* starting */, mDisplayContent,
true /* deferResume */);
+ if (mDisplayContent.topRunningActivity() == null) {
+ // The transition is ready on a display with no running activities.
+ mTransitionController.setReady(mDisplayContent);
+ }
}
if (activityRemoved) {
mRootWindowContainer.resumeFocusedTasksTopActivities();
@@ -6027,14 +6033,7 @@
true /* activityChange */, true /* updateOomAdj */,
true /* addPendingTopUid */);
}
- final ContentCaptureManagerInternal contentCaptureService =
- LocalServices.getService(ContentCaptureManagerInternal.class);
- if (contentCaptureService != null) {
- contentCaptureService.notifyActivityEvent(mUserId, mActivityComponent,
- ActivityEvent.TYPE_ACTIVITY_STARTED,
- new ActivityId(getTask() != null ? getTask().mTaskId : INVALID_TASK_ID,
- shareableActivityToken));
- }
+ mAtmService.mH.post(this::notifyActivityStartedToContentCaptureService);
break;
case PAUSED:
mAtmService.updateBatteryStats(this, false);
@@ -6066,6 +6065,22 @@
}
}
+ private void notifyActivityStartedToContentCaptureService() {
+ final ContentCaptureManagerInternal contentCaptureService =
+ LocalServices.getService(ContentCaptureManagerInternal.class);
+ if (contentCaptureService != null) {
+ // For ACTIVITY_STARTED content capture is directly invoked to avoid persisting
+ // to UsageStats.
+ contentCaptureService.notifyActivityEvent(mUserId, mActivityComponent,
+ ActivityEvent.TYPE_ACTIVITY_STARTED,
+ new ActivityId(getTask() != null ? getTask().mTaskId : INVALID_TASK_ID,
+ shareableActivityToken));
+
+ contentCaptureService.sendActivityStartAssistData(mUserId,
+ shareableActivityToken, intent);
+ }
+ }
+
State getState() {
return mState;
}
@@ -6547,7 +6562,10 @@
// Schedule an idle timeout in case the app doesn't do it for us.
mTaskSupervisor.scheduleIdleTimeout(this);
- mTaskSupervisor.reportResumedActivityLocked(this);
+ mTaskSupervisor.mStoppingActivities.remove(this);
+ if (getDisplayArea().allResumedActivitiesComplete()) {
+ mRootWindowContainer.executeAppTransitionForAllDisplay();
+ }
resumeKeyDispatchingLocked();
final Task rootTask = getRootTask();
@@ -8533,6 +8551,8 @@
mIsEligibleForFixedOrientationLetterbox = false;
mLetterboxBoundsForFixedOrientationAndAspectRatio = null;
mLetterboxBoundsForAspectRatio = null;
+ mResolveConfigHint.resolveTmpOverrides(mDisplayContent, newParentConfiguration,
+ isFixedRotationTransforming());
// Can't use resolvedConfig.windowConfiguration.getWindowingMode() because it can be
// different from windowing mode of the task (PiP) during transition from fullscreen to PiP
@@ -8647,10 +8667,15 @@
}
applySizeOverrideIfNeeded(newParentConfiguration, parentWindowingMode, resolvedConfig);
+ mResolveConfigHint.resetTmpOverrides();
logAppCompatState();
}
+ @Nullable Rect getParentAppBoundsOverride() {
+ return Rect.copyOrNull(mResolveConfigHint.mTmpParentAppBoundsOverride);
+ }
+
/**
* If necessary, override configuration fields related to app bounds.
* This will happen when the app is targeting SDK earlier than 35.
@@ -8674,8 +8699,12 @@
rotation = mDisplayContent.getRotation();
}
if (!mOptOutEdgeToEdge && (!mResolveConfigHint.mUseOverrideInsetsForConfig
- || getCompatDisplayInsets() != null || shouldCreateCompatDisplayInsets()
- || isFloating(parentWindowingMode) || rotation == ROTATION_UNDEFINED)) {
+ || getCompatDisplayInsets() != null
+ || (isFloating(parentWindowingMode)
+ // Check the windowing mode of activity as well in case it is switching
+ // between PiP and fullscreen.
+ && isFloating(inOutConfig.windowConfiguration.getWindowingMode()))
+ || rotation == ROTATION_UNDEFINED)) {
// If the insets configuration decoupled logic is not enabled for the app, or the app
// already has a compat override, or the context doesn't contain enough info to
// calculate the override, skip the override.
@@ -8697,7 +8726,7 @@
: mDisplayContent.mBaseDisplayWidth;
final int dh = rotated ? mDisplayContent.mBaseDisplayWidth
: mDisplayContent.mBaseDisplayHeight;
- final Rect nonDecorInsets = mDisplayContent.getDisplayPolicy()
+ final Rect nonDecorInsets = mDisplayContent.getDisplayPolicy()
.getDecorInsetsInfo(rotation, dw, dh).mOverrideNonDecorInsets;
// This should be the only place override the configuration for ActivityRecord. Override
// the value if not calculated yet.
@@ -8713,12 +8742,10 @@
}
density *= DisplayMetrics.DENSITY_DEFAULT_SCALE;
if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
- final int overrideScreenWidthDp = (int) (outAppBounds.width() / density + 0.5f);
- inOutConfig.screenWidthDp = overrideScreenWidthDp;
+ inOutConfig.screenWidthDp = (int) (outAppBounds.width() / density + 0.5f);
}
if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
- final int overrideScreenHeightDp = (int) (outAppBounds.height() / density + 0.5f);
- inOutConfig.screenHeightDp = overrideScreenHeightDp;
+ inOutConfig.screenHeightDp = (int) (outAppBounds.height() / density + 0.5f);
}
if (inOutConfig.smallestScreenWidthDp
== Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED
@@ -8829,7 +8856,7 @@
}
final Rect screenResolvedBounds =
mSizeCompatBounds != null ? mSizeCompatBounds : resolvedBounds;
- final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds();
+ final Rect parentAppBounds = mResolveConfigHint.mTmpParentAppBoundsOverride;
final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
final float screenResolvedBoundsWidth = screenResolvedBounds.width();
final float parentAppBoundsWidth = parentAppBounds.width();
@@ -9238,7 +9265,7 @@
*/
private void resolveAspectRatioRestriction(Configuration newParentConfiguration) {
final Configuration resolvedConfig = getResolvedOverrideConfiguration();
- final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds();
+ final Rect parentAppBounds = mResolveConfigHint.mTmpParentAppBoundsOverride;
final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
// Use tmp bounds to calculate aspect ratio so we can know whether the activity should use
@@ -9267,19 +9294,6 @@
@NonNull CompatDisplayInsets compatDisplayInsets) {
final Configuration resolvedConfig = getResolvedOverrideConfiguration();
final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
- final Insets insets;
- if (mResolveConfigHint.mUseOverrideInsetsForConfig) {
- // TODO(b/343197837): Add test to verify SCM behaviour with new bound configuration
- // Insets are decoupled from configuration by default from V+, use legacy
- // compatibility behaviour for apps targeting SDK earlier than 35
- // (see applySizeOverrideIfNeeded).
- insets = Insets.of(mDisplayContent.getDisplayPolicy()
- .getDecorInsetsInfo(mDisplayContent.mDisplayFrames.mRotation,
- mDisplayContent.mDisplayFrames.mWidth,
- mDisplayContent.mDisplayFrames.mHeight).mOverrideNonDecorInsets);
- } else {
- insets = Insets.NONE;
- }
// When an activity needs to be letterboxed because of fixed orientation, use fixed
// orientation bounds (stored in resolved bounds) instead of parent bounds since the
@@ -9290,22 +9304,22 @@
final Rect containerBounds = useResolvedBounds
? new Rect(resolvedBounds)
: newParentConfiguration.windowConfiguration.getBounds();
- final Rect parentAppBounds =
- newParentConfiguration.windowConfiguration.getAppBounds();
- parentAppBounds.inset(insets);
final Rect containerAppBounds = useResolvedBounds
? new Rect(resolvedConfig.windowConfiguration.getAppBounds())
- : parentAppBounds;
+ : mResolveConfigHint.mTmpParentAppBoundsOverride;
final int requestedOrientation = getRequestedConfigurationOrientation();
final boolean orientationRequested = requestedOrientation != ORIENTATION_UNDEFINED;
+ final int parentOrientation = mResolveConfigHint.mUseOverrideInsetsForConfig
+ ? mResolveConfigHint.mTmpOverrideConfigOrientation
+ : newParentConfiguration.orientation;
final int orientation = orientationRequested
? requestedOrientation
// We should use the original orientation of the activity when possible to avoid
// forcing the activity in the opposite orientation.
: compatDisplayInsets.mOriginalRequestedOrientation != ORIENTATION_UNDEFINED
? compatDisplayInsets.mOriginalRequestedOrientation
- : newParentConfiguration.orientation;
+ : parentOrientation;
int rotation = newParentConfiguration.windowConfiguration.getRotation();
final boolean isFixedToUserRotation = mDisplayContent == null
|| mDisplayContent.getDisplayRotation().isFixedToUserRotation();
@@ -9347,7 +9361,7 @@
// Use parent orientation if it cannot be decided by bounds, so the activity can fit inside
// the parent bounds appropriately.
if (resolvedConfig.screenWidthDp == resolvedConfig.screenHeightDp) {
- resolvedConfig.orientation = newParentConfiguration.orientation;
+ resolvedConfig.orientation = parentOrientation;
}
// Below figure is an example that puts an activity which was launched in a larger container
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index d7a696f..e6d8132 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -139,6 +139,7 @@
import com.android.server.wm.BackgroundActivityStartController.BalVerdict;
import com.android.server.wm.LaunchParamsController.LaunchParams;
import com.android.server.wm.TaskFragment.EmbeddingCheckResult;
+import com.android.wm.shell.Flags;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -1723,7 +1724,14 @@
// Get top task at beginning because the order may be changed when reusing existing task.
final Task prevTopRootTask = mPreferredTaskDisplayArea.getFocusedRootTask();
final Task prevTopTask = prevTopRootTask != null ? prevTopRootTask.getTopLeafTask() : null;
- final Task reusedTask = resolveReusableTask();
+ final boolean sourceActivityLaunchedFromBubble =
+ sourceRecord != null && sourceRecord.getLaunchedFromBubble();
+ // if the flag is enabled, allow reusing bubbled tasks only if the source activity is
+ // bubbled.
+ final boolean includeLaunchedFromBubble =
+ Flags.onlyReuseBubbledTaskWhenLaunchedFromBubble()
+ ? sourceActivityLaunchedFromBubble : true;
+ final Task reusedTask = resolveReusableTask(includeLaunchedFromBubble);
// If requested, freeze the task list
if (mOptions != null && mOptions.freezeRecentTasksReordering()
@@ -2722,8 +2730,11 @@
/**
* Decide whether the new activity should be inserted into an existing task. Returns null
* if not or an ActivityRecord with the task into which the new activity should be added.
+ *
+ * @param includeLaunchedFromBubble whether a task whose top activity was launched from a bubble
+ * should be allowed to be reused for the new activity.
*/
- private Task resolveReusableTask() {
+ private Task resolveReusableTask(boolean includeLaunchedFromBubble) {
// If a target task is specified, try to reuse that one
if (mOptions != null && mOptions.getLaunchTaskId() != INVALID_TASK_ID) {
Task launchTask = mRootWindowContainer.anyTaskForId(mOptions.getLaunchTaskId());
@@ -2767,7 +2778,8 @@
} else {
// Otherwise find the best task to put the activity in.
intentActivity =
- mRootWindowContainer.findTask(mStartActivity, mPreferredTaskDisplayArea);
+ mRootWindowContainer.findTask(mStartActivity, mPreferredTaskDisplayArea,
+ includeLaunchedFromBubble);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 3867d2d..b6e6991 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -2064,21 +2064,6 @@
}
}
- boolean reportResumedActivityLocked(ActivityRecord r) {
- // A resumed activity cannot be stopping. remove from list
- mStoppingActivities.remove(r);
-
- final Task rootTask = r.getRootTask();
- if (rootTask.getDisplayArea().allResumedActivitiesComplete()) {
- mRootWindowContainer.ensureActivitiesVisible();
- // Make sure activity & window visibility should be identical
- // for all displays in this stage.
- mRootWindowContainer.executeAppTransitionForAllDisplay();
- return true;
- }
- return false;
- }
-
// Called when WindowManager has finished animating the launchingBehind activity to the back.
private void handleLaunchTaskBehindCompleteLocked(ActivityRecord r) {
final Task task = r.getTask();
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index f91ef1d..0febec9 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -200,6 +200,7 @@
}
infoBuilder.setOnBackInvokedCallback(callbackInfo.getCallback());
infoBuilder.setAnimationCallback(callbackInfo.isAnimationCallback());
+ infoBuilder.setTouchableRegion(window.getFrame());
mNavigationMonitor.startMonitor(window, navigationObserver);
ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation currentTask=%s, "
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index a3a6b51..87ee5d8 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -5719,15 +5719,21 @@
* @see Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
*/
boolean supportsSystemDecorations() {
+ boolean forceDesktopModeOnDisplay = forceDesktopMode();
+
+ if (com.android.window.flags.Flags.rearDisplayDisableForceDesktopSystemDecorations()) {
+ // System decorations should not be forced on a rear display due to security policies.
+ forceDesktopModeOnDisplay =
+ forceDesktopModeOnDisplay && ((mDisplay.getFlags() & Display.FLAG_REAR) == 0);
+ }
+
return (mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(this)
|| (mDisplay.getFlags() & FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0
- || forceDesktopMode())
+ || forceDesktopModeOnDisplay)
// VR virtual display will be used to run and render 2D app within a VR experience.
&& mDisplayId != mWmService.mVr2dDisplayId
// Do not show system decorations on untrusted virtual display.
- && isTrusted()
- // No system decoration on rear display.
- && (mDisplay.getFlags() & Display.FLAG_REAR) == 0;
+ && isTrusted();
}
/**
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index 0978cb4..a21ba26 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -77,12 +77,14 @@
mWindows.put(inputToken, window);
final InputTransferToken inputTransferToken = window.getInputTransferToken();
mWindowsByInputTransferToken.put(inputTransferToken, window);
- mWindowsByWindowToken.put(window.getWindowToken(), window);
+ final IBinder windowToken = window.getWindowToken();
+ mWindowsByWindowToken.put(windowToken, window);
updateProcessController(window);
window.mClient.linkToDeath(()-> {
synchronized (mGlobalLock) {
mWindows.remove(inputToken);
mWindowsByInputTransferToken.remove(inputTransferToken);
+ mWindowsByWindowToken.remove(windowToken);
}
}, 0);
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
index b2ba9d1..bf99ccd 100644
--- a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
+++ b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
@@ -20,6 +20,7 @@
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.ViewRootImpl.CLIENT_TRANSIENT;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID;
@@ -35,6 +36,7 @@
import android.content.IntentFilter;
import android.graphics.Insets;
import android.graphics.PixelFormat;
+import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.os.Binder;
import android.os.Bundle;
@@ -60,6 +62,7 @@
import android.view.animation.Interpolator;
import android.widget.Button;
import android.widget.FrameLayout;
+import android.widget.RelativeLayout;
import com.android.internal.R;
@@ -233,6 +236,7 @@
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
PixelFormat.TRANSLUCENT);
lp.setFitInsetsTypes(lp.getFitInsetsTypes() & ~Type.statusBars());
+ lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
// Trusted overlay so touches outside the touchable area are allowed to pass through
lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
| WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
@@ -245,13 +249,20 @@
private FrameLayout.LayoutParams getBubbleLayoutParams() {
return new FrameLayout.LayoutParams(
- mContext.getResources().getDimensionPixelSize(
- R.dimen.immersive_mode_cling_width),
+ getClingWindowWidth(),
ViewGroup.LayoutParams.WRAP_CONTENT,
Gravity.CENTER_HORIZONTAL | Gravity.TOP);
}
/**
+ * Returns the width of the cling window.
+ */
+ private int getClingWindowWidth() {
+ return mContext.getResources().getDimensionPixelSize(
+ R.dimen.immersive_mode_cling_width);
+ }
+
+ /**
* @return the window token that's used by all ImmersiveModeConfirmation windows.
*/
IBinder getWindowToken() {
@@ -387,6 +398,24 @@
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ // If the top display cutout overlaps with the full-width (windowWidth=-1)/centered
+ // dialog, then adjust the dialog contents by the cutout
+ final int width = getWidth();
+ final int windowWidth = getClingWindowWidth();
+ final Rect topDisplayCutout = insets.getDisplayCutout() != null
+ ? insets.getDisplayCutout().getBoundingRectTop()
+ : new Rect();
+ final boolean intersectsTopCutout = topDisplayCutout.intersects(
+ width - (windowWidth / 2), 0,
+ width + (windowWidth / 2), topDisplayCutout.bottom);
+ if (mClingWindow != null &&
+ (windowWidth < 0 || (width > 0 && intersectsTopCutout))) {
+ final View iconView = mClingWindow.findViewById(R.id.immersive_cling_icon);
+ RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams)
+ iconView.getLayoutParams();
+ lp.topMargin = topDisplayCutout.bottom;
+ iconView.setLayoutParams(lp);
+ }
// we will be hiding the nav bar, so layout as if it's already hidden
return new WindowInsets.Builder(insets).setInsets(
Type.systemBars(), Insets.NONE).build();
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 3c556bf..6288a42 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -424,7 +424,9 @@
// Use task bounds to calculating rounded corners if the task is not floating.
final InsetsState state = copyState ? new InsetsState(originalState)
: originalState;
- state.setRoundedCornerFrame(task.getBounds());
+ state.setRoundedCornerFrame(token.isFixedRotationTransforming()
+ ? token.getFixedRotationTransformDisplayBounds()
+ : task.getBounds());
return state;
}
}
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index c683d4d..f70d2a5 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -538,13 +538,6 @@
|| !mWindowManager.isKeyguardSecure(mService.getCurrentUserId());
}
- /**
- * @return Whether the dream activity is on top of default display.
- */
- boolean isShowingDream() {
- return getDisplayState(DEFAULT_DISPLAY).mShowingDream;
- }
-
private void updateKeyguardSleepToken() {
for (int displayNdx = mRootWindowContainer.getChildCount() - 1;
displayNdx >= 0; displayNdx--) {
@@ -631,7 +624,6 @@
* Note that this can be true even if the keyguard is disabled or not showing.
*/
private boolean mOccluded;
- private boolean mShowingDream;
private ActivityRecord mTopOccludesActivity;
private ActivityRecord mDismissingKeyguardActivity;
@@ -669,7 +661,6 @@
mRequestDismissKeyguard = false;
mOccluded = false;
- mShowingDream = false;
mTopOccludesActivity = null;
mDismissingKeyguardActivity = null;
@@ -697,21 +688,18 @@
// Only the top activity may control occluded, as we can't occlude the Keyguard
// if the top app doesn't want to occlude it.
- occludedByActivity = mTopOccludesActivity != null
+ mOccluded = mTopOccludesActivity != null
|| (mDismissingKeyguardActivity != null
&& task.topRunningActivity() == mDismissingKeyguardActivity
&& controller.canShowWhileOccluded(
true /* dismissKeyguard */, false /* showWhenLocked */));
// FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD only apply for secondary display.
if (mDisplayId != DEFAULT_DISPLAY) {
- occludedByActivity |= display.canShowWithInsecureKeyguard()
+ mOccluded |= display.canShowWithInsecureKeyguard()
&& controller.canDismissKeyguard();
}
}
- mShowingDream = display.getDisplayPolicy().isShowingDreamLw() && (top != null
- && top.getActivityType() == ACTIVITY_TYPE_DREAM);
- mOccluded = mShowingDream || occludedByActivity;
mRequestDismissKeyguard = lastDismissKeyguardActivity != mDismissingKeyguardActivity
&& !mOccluded && !mKeyguardGoingAway
&& mDismissingKeyguardActivity != null;
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 194771f..5e93e89 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -1288,6 +1288,9 @@
if (!allowHorizontalReachabilityForThinLetterbox()) {
return false;
}
+ final Rect parentAppBoundsOverride = mActivityRecord.getParentAppBoundsOverride();
+ final Rect parentAppBounds = parentAppBoundsOverride != null
+ ? parentAppBoundsOverride : parentConfiguration.windowConfiguration.getAppBounds();
// Use screen resolved bounds which uses resolved bounds or size compat bounds
// as activity bounds can sometimes be empty
final Rect opaqueActivityBounds = mActivityRecord.mTransparentPolicy
@@ -1297,10 +1300,8 @@
&& parentConfiguration.windowConfiguration.getWindowingMode()
== WINDOWING_MODE_FULLSCREEN
// Check whether the activity fills the parent vertically.
- && parentConfiguration.windowConfiguration.getAppBounds().height()
- <= opaqueActivityBounds.height()
- && parentConfiguration.windowConfiguration.getAppBounds().width()
- > opaqueActivityBounds.width();
+ && parentAppBounds.height() <= opaqueActivityBounds.height()
+ && parentAppBounds.width() > opaqueActivityBounds.width();
}
@VisibleForTesting
@@ -1326,6 +1327,9 @@
if (!allowVerticalReachabilityForThinLetterbox()) {
return false;
}
+ final Rect parentAppBoundsOverride = mActivityRecord.getParentAppBoundsOverride();
+ final Rect parentAppBounds = parentAppBoundsOverride != null
+ ? parentAppBoundsOverride : parentConfiguration.windowConfiguration.getAppBounds();
// Use screen resolved bounds which uses resolved bounds or size compat bounds
// as activity bounds can sometimes be empty.
final Rect opaqueActivityBounds = mActivityRecord.mTransparentPolicy
@@ -1335,10 +1339,8 @@
&& parentConfiguration.windowConfiguration.getWindowingMode()
== WINDOWING_MODE_FULLSCREEN
// Check whether the activity fills the parent horizontally.
- && parentConfiguration.windowConfiguration.getAppBounds().width()
- <= opaqueActivityBounds.width()
- && parentConfiguration.windowConfiguration.getAppBounds().height()
- > opaqueActivityBounds.height();
+ && parentAppBounds.width() <= opaqueActivityBounds.width()
+ && parentAppBounds.height() > opaqueActivityBounds.height();
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 9c7c41c..54ba47e 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -314,13 +314,19 @@
private boolean isDocument;
private Uri documentData;
- void init(int activityType, String taskAffinity, Intent intent, ActivityInfo info) {
+ // determines whether to include bubbled tasks. defaults to true to preserve previous
+ // behavior.
+ private boolean mIncludeLaunchedFromBubble = true;
+
+ void init(int activityType, String taskAffinity, Intent intent, ActivityInfo info,
+ boolean includeLaunchedFromBubble) {
mActivityType = activityType;
mTaskAffinity = taskAffinity;
mIntent = intent;
mInfo = info;
mIdealRecord = null;
mCandidateRecord = null;
+ mIncludeLaunchedFromBubble = includeLaunchedFromBubble;
}
/**
@@ -362,7 +368,8 @@
}
// Overlays should not be considered as the task's logical top activity.
- final ActivityRecord r = task.getTopNonFinishingActivity(false /* includeOverlays */);
+ final ActivityRecord r = task.getTopNonFinishingActivity(
+ false /* includeOverlays */, mIncludeLaunchedFromBubble);
if (r == null || r.finishing || r.mUserId != userId
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
@@ -1898,6 +1905,7 @@
// Don't do recursive work.
return;
}
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RWC_ensureActivitiesVisible");
mTaskSupervisor.beginActivityVisibilityUpdate();
try {
// First the front root tasks. In case any are not fullscreen and are in front of home.
@@ -1907,6 +1915,7 @@
}
} finally {
mTaskSupervisor.endActivityVisibilityUpdate();
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
@@ -2370,18 +2379,20 @@
}
@Nullable
- ActivityRecord findTask(ActivityRecord r, TaskDisplayArea preferredTaskDisplayArea) {
+ ActivityRecord findTask(ActivityRecord r, TaskDisplayArea preferredTaskDisplayArea,
+ boolean includeLaunchedFromBubble) {
return findTask(r.getActivityType(), r.taskAffinity, r.intent, r.info,
- preferredTaskDisplayArea);
+ preferredTaskDisplayArea, includeLaunchedFromBubble);
}
@Nullable
ActivityRecord findTask(int activityType, String taskAffinity, Intent intent, ActivityInfo info,
- TaskDisplayArea preferredTaskDisplayArea) {
+ TaskDisplayArea preferredTaskDisplayArea, boolean includeLaunchedFromBubble) {
ProtoLog.d(WM_DEBUG_TASKS, "Looking for task of type=%s, taskAffinity=%s, intent=%s"
- + ", info=%s, preferredTDA=%s", activityType, taskAffinity, intent, info,
- preferredTaskDisplayArea);
- mTmpFindTaskResult.init(activityType, taskAffinity, intent, info);
+ + ", info=%s, preferredTDA=%s, includeLaunchedFromBubble=%b", activityType,
+ taskAffinity, intent, info, preferredTaskDisplayArea, includeLaunchedFromBubble);
+ mTmpFindTaskResult.init(activityType, taskAffinity, intent, info,
+ includeLaunchedFromBubble);
// Looking up task on preferred display area first
ActivityRecord candidateActivity = null;
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index 1cc1a57..7510180 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -157,7 +157,7 @@
// home & recent tasks
return;
}
- if (task.isVisible()) {
+ if (task.isVisibleRequested()) {
mTmpVisibleTasks.add(task);
} else {
mTmpInvisibleTasks.add(task);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 13883fd..22f718d 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -445,6 +445,7 @@
int mPrevDisplayId = INVALID_DISPLAY;
int mMultiWindowRestoreWindowingMode = INVALID_WINDOWING_MODE;
+ WindowContainerToken mMultiWindowRestoreParent;
/**
* Last requested orientation reported to DisplayContent. This is different from {@link
@@ -4634,6 +4635,25 @@
return TASK;
}
+ /**
+ * Restores to the windowing mode saved when task requested to enter fullscreen using
+ * {@link Activity#requestFullscreenMode} API if it is valid. The task is also reparented to
+ * the previous parent if parent has changed.
+ */
+ void restoreWindowingMode() {
+ if (mMultiWindowRestoreWindowingMode == INVALID_WINDOWING_MODE) {
+ return;
+ }
+ if (!getParent().mRemoteToken.toWindowContainerToken()
+ .equals(mMultiWindowRestoreParent)) {
+ // Restore previous parent if parent has changed.
+ final Task parent = fromWindowContainerToken(mMultiWindowRestoreParent);
+ reparent(parent, MAX_VALUE);
+ }
+
+ setWindowingMode(mMultiWindowRestoreWindowingMode);
+ }
+
@Override
public void setWindowingMode(int windowingMode) {
// Calling Task#setWindowingMode() for leaf task since this is a specialization of
@@ -4766,6 +4786,12 @@
if (com.android.window.flags.Flags.removePrepareSurfaceInPlacement()
&& lastParentBeforePip.mSyncState == SYNC_STATE_NONE) {
lastParentBeforePip.prepareSurfaces();
+ // If the moveToFront is a part of finishing transition, then make sure
+ // the z-order of tasks are up-to-date.
+ if (topActivity.mTransitionController.inFinishingTransition(topActivity)) {
+ Transition.assignLayers(taskDisplayArea,
+ taskDisplayArea.getPendingTransaction());
+ }
}
}
if (isPip2ExperimentEnabled) {
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index b8b746a..ab72e3c 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -40,6 +40,8 @@
import static android.os.Process.SYSTEM_UID;
import static android.os.UserHandle.USER_NULL;
import static android.view.Display.INVALID_DISPLAY;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
@@ -87,6 +89,7 @@
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
+import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.IBinder;
@@ -1101,21 +1104,34 @@
}
ActivityRecord getTopNonFinishingActivity() {
- return getTopNonFinishingActivity(true /* includeOverlays */);
+ return getTopNonFinishingActivity(
+ true /* includeOverlays */, true /* includeLaunchedFromBubble */);
}
/**
* Returns the top-most non-finishing activity, even if the activity is NOT ok to show to
* the current user.
* @param includeOverlays whether the task overlay activity should be included.
+ * @param includeLaunchedFromBubble whether activities that were launched from a bubble should
+ * be included.
* @see #topRunningActivity(boolean)
*/
- ActivityRecord getTopNonFinishingActivity(boolean includeOverlays) {
- // Split into 2 to avoid object creation due to variable capture.
+ ActivityRecord getTopNonFinishingActivity(boolean includeOverlays,
+ boolean includeLaunchedFromBubble) {
+ // Split to avoid object creation due to variable capture.
if (includeOverlays) {
- return getActivity((r) -> !r.finishing);
+ if (includeLaunchedFromBubble) {
+ return getActivity(r -> !r.finishing);
+ } else {
+ return getActivity(r -> !r.finishing && !r.getLaunchedFromBubble());
+ }
}
- return getActivity((r) -> !r.finishing && !r.isTaskOverlay());
+ if (includeLaunchedFromBubble) {
+ return getActivity(r -> !r.finishing && !r.isTaskOverlay());
+ } else {
+ return getActivity(
+ r -> !r.finishing && !r.isTaskOverlay() && !r.getLaunchedFromBubble());
+ }
}
ActivityRecord topRunningActivity() {
@@ -2225,7 +2241,43 @@
static class ConfigOverrideHint {
@Nullable DisplayInfo mTmpOverrideDisplayInfo;
@Nullable ActivityRecord.CompatDisplayInsets mTmpCompatInsets;
+ @Nullable Rect mTmpParentAppBoundsOverride;
+ int mTmpOverrideConfigOrientation;
boolean mUseOverrideInsetsForConfig;
+
+ void resolveTmpOverrides(DisplayContent dc, Configuration parentConfig,
+ boolean isFixedRotationTransforming) {
+ mTmpParentAppBoundsOverride = new Rect(parentConfig.windowConfiguration.getAppBounds());
+ final Insets insets;
+ if (mUseOverrideInsetsForConfig && dc != null) {
+ // Insets are decoupled from configuration by default from V+, use legacy
+ // compatibility behaviour for apps targeting SDK earlier than 35
+ // (see applySizeOverrideIfNeeded).
+ int rotation = parentConfig.windowConfiguration.getRotation();
+ if (rotation == ROTATION_UNDEFINED && !isFixedRotationTransforming) {
+ rotation = dc.getRotation();
+ }
+ final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
+ final int dw = rotated ? dc.mBaseDisplayHeight : dc.mBaseDisplayWidth;
+ final int dh = rotated ? dc.mBaseDisplayWidth : dc.mBaseDisplayHeight;
+ DisplayPolicy.DecorInsets.Info decorInsets = dc.getDisplayPolicy()
+ .getDecorInsetsInfo(rotation, dw, dh);
+ final Rect stableBounds = decorInsets.mOverrideConfigFrame;
+ mTmpOverrideConfigOrientation = stableBounds.width() > stableBounds.height()
+ ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
+ insets = Insets.of(decorInsets.mOverrideNonDecorInsets);
+ } else {
+ insets = Insets.NONE;
+ }
+ mTmpParentAppBoundsOverride.inset(insets);
+ }
+
+ void resetTmpOverrides() {
+ mTmpOverrideDisplayInfo = null;
+ mTmpCompatInsets = null;
+ mTmpParentAppBoundsOverride = null;
+ mTmpOverrideConfigOrientation = ORIENTATION_UNDEFINED;
+ }
}
void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
@@ -2311,7 +2363,9 @@
if (!customContainerPolicy && windowingMode != WINDOWING_MODE_FREEFORM) {
final Rect containingAppBounds;
if (insideParentBounds) {
- containingAppBounds = parentConfig.windowConfiguration.getAppBounds();
+ containingAppBounds = useOverrideInsetsForConfig
+ ? overrideHint.mTmpParentAppBoundsOverride
+ : parentConfig.windowConfiguration.getAppBounds();
} else {
// Restrict appBounds to display non-decor rather than parent because the
// override bounds are beyond the parent. Otherwise, it won't match the
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 4aa3e36..63ca469 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1048,7 +1048,7 @@
// the animation played. This puts the layers back into the correct order.
for (int i = displays.size() - 1; i >= 0; --i) {
if (displays.valueAt(i) == null) continue;
- updateDisplayLayers(displays.valueAt(i), t);
+ assignLayers(displays.valueAt(i), t);
}
for (int i = 0; i < info.getRootCount(); ++i) {
@@ -1056,12 +1056,13 @@
}
}
- private static void updateDisplayLayers(DisplayContent dc, SurfaceControl.Transaction t) {
- dc.mTransitionController.mBuildingFinishLayers = true;
+ /** Assigns the layers for the start or end state of transition. */
+ static void assignLayers(WindowContainer<?> wc, SurfaceControl.Transaction t) {
+ wc.mTransitionController.mBuildingFinishLayers = true;
try {
- dc.assignChildLayers(t);
+ wc.assignChildLayers(t);
} finally {
- dc.mTransitionController.mBuildingFinishLayers = false;
+ wc.mTransitionController.mBuildingFinishLayers = false;
}
}
@@ -2717,7 +2718,7 @@
rootLeash.setUnreleasedWarningCallSite("Transition.calculateTransitionRoots");
// Update layers to start transaction because we prevent assignment during collect, so
// the layer of transition root can be correct.
- updateDisplayLayers(dc, startT);
+ assignLayers(dc, startT);
startT.setLayer(rootLeash, leashReference.getLastLayer());
outInfo.addRootLeash(endDisplayId, rootLeash,
ancestor.getBounds().left, ancestor.getBounds().top);
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index edbba92..70143ba 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1077,9 +1077,6 @@
if (dc != null && dc != this) {
dc.getPendingTransaction().merge(mPendingTransaction);
}
- if (dc != this && mLocalInsetsSources != null) {
- mLocalInsetsSources.clear();
- }
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowContainer child = mChildren.get(i);
child.onDisplayChanged(dc);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8a6c73a..b814ccd 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3502,10 +3502,11 @@
if (!checkCallingPermission(permission.CONTROL_KEYGUARD, "dismissKeyguard")) {
throw new SecurityException("Requires CONTROL_KEYGUARD permission");
}
- if (!dreamHandlesConfirmKeys() && mAtmService.mKeyguardController.isShowingDream()) {
- mAtmService.mTaskSupervisor.wakeUp("leaveDream");
- }
synchronized (mGlobalLock) {
+ if (!dreamHandlesConfirmKeys()
+ && getDefaultDisplayContentLocked().getDisplayPolicy().isShowingDreamLw()) {
+ mAtmService.mTaskSupervisor.wakeUp("leaveDream");
+ }
mPolicy.dismissKeyguardLw(callback, message);
}
}
diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
index f47a59d..4be21d8 100644
--- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp
+++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
@@ -131,17 +131,28 @@
}
std::function<void()> createCallback(jlong vibrationId) {
- return [vibrationId, this]() {
+ auto callbackId = ++mCallbackId;
+ return [vibrationId, callbackId, this]() {
+ auto currentCallbackId = mCallbackId.load();
+ if (currentCallbackId != callbackId) {
+ // This callback is from an older HAL call that is no longer relevant to the service
+ return;
+ }
auto jniEnv = GetOrAttachJNIEnvironment(sJvm);
jniEnv->CallVoidMethod(mCallbackListener, sMethodIdOnComplete, mVibratorId,
vibrationId);
};
}
+ void disableOldCallbacks() {
+ mCallbackId++;
+ }
+
private:
const std::shared_ptr<vibrator::HalController> mHal;
const int32_t mVibratorId;
const jobject mCallbackListener;
+ std::atomic<int64_t> mCallbackId;
};
static aidl::BrakingPwle brakingPwle(aidl::Braking braking, int32_t duration) {
@@ -236,6 +247,7 @@
}
auto offFn = [](vibrator::HalWrapper* hal) { return hal->off(); };
wrapper->halCall<void>(offFn, "off");
+ wrapper->disableOldCallbacks();
}
static void vibratorSetAmplitude(JNIEnv* env, jclass /* clazz */, jlong ptr, jfloat amplitude) {
diff --git a/services/incremental/Android.bp b/services/incremental/Android.bp
index 957d7c3..ce05c82 100644
--- a/services/incremental/Android.bp
+++ b/services/incremental/Android.bp
@@ -66,7 +66,6 @@
"libprotobuf-cpp-lite",
"service.incremental.proto",
"libvold_binder",
- "libc++fs",
"libziparchive_for_incfs",
],
shared_libs: [
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index cfe4e17..107c294 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1196,11 +1196,12 @@
mSystemServiceManager.startService(RecoverySystemService.Lifecycle.class);
t.traceEnd();
+ // Initialize RescueParty.
+ RescueParty.registerHealthObserver(mSystemContext);
if (!Flags.recoverabilityDetection()) {
// Now that we have the bare essentials of the OS up and running, take
// note that we just booted, which might send out a rescue party if
// we're stuck in a runtime restart loop.
- RescueParty.registerHealthObserver(mSystemContext);
PackageWatchdog.getInstance(mSystemContext).noteBoot();
}
@@ -2917,10 +2918,10 @@
t.traceEnd();
if (Flags.recoverabilityDetection()) {
- // Now that we have the essential services needed for rescue party, initialize
- // RescuParty. note that we just booted, which might send out a rescue party if
- // we're stuck in a runtime restart loop.
- RescueParty.registerHealthObserver(mSystemContext);
+ // Now that we have the essential services needed for mitigations, register the boot
+ // with package watchdog.
+ // Note that we just booted, which might send out a rescue party if we're stuck in a
+ // runtime restart loop.
PackageWatchdog.getInstance(mSystemContext).noteBoot();
}
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index 6499556..78dbc60 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -533,8 +533,7 @@
val packageState =
packageManagerLocal.withFilteredSnapshot(Binder.getCallingUid(), userId).use {
it.getPackageState(packageName)
- }
- ?: return PackageManager.PERMISSION_DENIED
+ } ?: return PackageManager.PERMISSION_DENIED
val isPermissionGranted =
service.getState { isPermissionGranted(packageState, userId, permissionName, deviceId) }
@@ -1164,8 +1163,7 @@
val packageState =
packageManagerLocal.withFilteredSnapshot(Binder.getCallingUid(), userId).use {
it.getPackageState(packageName)
- }
- ?: return false
+ } ?: return false
service.getState {
if (isPermissionGranted(packageState, userId, permissionName, deviceId)) {
@@ -1216,8 +1214,7 @@
val packageState =
packageManagerLocal.withFilteredSnapshot(callingUid, userId).use {
it.getPackageState(packageName)
- }
- ?: return false
+ } ?: return false
val appId = packageState.appId
if (UserHandle.getAppId(callingUid) != appId) {
return false
@@ -1546,8 +1543,7 @@
val packageState =
packageManagerLocal.withFilteredSnapshot(callingUid, userId).use {
it.getPackageState(packageName)
- }
- ?: return null
+ } ?: return null
val androidPackage = packageState.androidPackage ?: return null
val isCallerPrivileged =
@@ -1710,8 +1706,7 @@
PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER,
userId
)
- ?.let { ArraySet(permissionNames).apply { this += it }.toList() }
- ?: permissionNames
+ ?.let { ArraySet(permissionNames).apply { this += it }.toList() } ?: permissionNames
setAllowlistedRestrictedPermissionsUnchecked(
androidPackage,
@@ -2753,27 +2748,25 @@
) {
return false
}
- return try {
- val contentResolver = context.contentResolver
- val userId = UserHandle.getUserId(uid)
- val isInSetup =
- Settings.Secure.getIntForUser(
- contentResolver,
- Settings.Secure.USER_SETUP_COMPLETE,
- userId
- ) == 0
- val isInDeferredSetup =
- Settings.Secure.getIntForUser(
- contentResolver,
- Settings.Secure.USER_SETUP_PERSONALIZATION_STATE,
- userId
- ) == Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED
- isInSetup || isInDeferredSetup
- } catch (e: Settings.SettingNotFoundException) {
- Slog.w(LOG_TAG, "Failed to check if the user is in restore: $e")
- false
- }
+
+ val userId = UserHandle.getUserId(uid)
+
+ val isInSetup = getSecureInt(Settings.Secure.USER_SETUP_COMPLETE, userId) == 0
+ if (isInSetup) return true
+
+ val isInDeferredSetup =
+ getSecureInt(Settings.Secure.USER_SETUP_PERSONALIZATION_STATE, userId) ==
+ Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED
+ return isInDeferredSetup
}
+
+ private fun getSecureInt(settingName: String, userId: Int): Int? =
+ try {
+ Settings.Secure.getIntForUser(context.contentResolver, settingName, userId)
+ } catch (e: Settings.SettingNotFoundException) {
+ Slog.i(LOG_TAG, "Setting $settingName not found", e)
+ null
+ }
}
private class OnPermissionsChangeListeners(looper: Looper) : Handler(looper) {
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml
index 820628c..8e6954b 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml
@@ -25,6 +25,12 @@
<option name="test-file-name" value="FrameworksImeTests.apk" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
+ <option name="run-command" value="wm dismiss-keyguard" />
+ <option name="run-command" value="settings put secure immersive_mode_confirmations confirmed" />
+ </target_preparer>
+
<option name="test-tag" value="FrameworksImeTests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
index 1535298..2029b71 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
@@ -48,6 +48,7 @@
import com.android.apps.inputmethod.simpleime.ims.InputMethodServiceWrapper;
import com.android.apps.inputmethod.simpleime.testing.TestActivity;
+import com.android.compatibility.common.util.SystemUtil;
import com.android.internal.inputmethod.InputMethodNavButtonFlags;
import org.junit.After;
@@ -834,8 +835,7 @@
private String executeShellCommand(String cmd) throws IOException {
Log.i(TAG, "Run command: " + cmd);
- return UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
- .executeShellCommand(cmd);
+ return SystemUtil.runShellCommandOrThrow(cmd);
}
private void clickOnEditorText() {
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
index 221a991..a4ca317 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
@@ -68,12 +68,15 @@
public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTestBase {
private DefaultImeVisibilityApplier mVisibilityApplier;
+ private int mUserId = 0;
+
@Before
public void setUp() throws RemoteException {
super.setUp();
mVisibilityApplier =
(DefaultImeVisibilityApplier) mInputMethodManagerService.getVisibilityApplier();
synchronized (ImfLock.class) {
+ mUserId = mInputMethodManagerService.getCurrentImeUserIdLocked();
mInputMethodManagerService.setAttachedClientForTesting(requireNonNull(
mInputMethodManagerService.getClientStateLocked(mMockInputMethodClient)));
}
@@ -103,7 +106,7 @@
assertThrows(IllegalArgumentException.class, () -> {
synchronized (ImfLock.class) {
mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(),
- STATE_INVALID);
+ STATE_INVALID, mUserId);
}
});
}
@@ -112,7 +115,8 @@
public void testApplyImeVisibility_showIme() {
final var statsToken = ImeTracker.Token.empty();
synchronized (ImfLock.class) {
- mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_SHOW_IME);
+ mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_SHOW_IME,
+ mUserId);
}
verify(mMockWindowManagerInternal).showImePostLayout(eq(mWindowToken), eq(statsToken));
}
@@ -121,7 +125,8 @@
public void testApplyImeVisibility_hideIme() {
final var statsToken = ImeTracker.Token.empty();
synchronized (ImfLock.class) {
- mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_HIDE_IME);
+ mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_HIDE_IME,
+ mUserId);
}
verify(mMockWindowManagerInternal).hideIme(eq(mWindowToken), anyInt() /* displayId */,
eq(statsToken));
@@ -132,7 +137,7 @@
mInputMethodManagerService.mImeWindowVis = IME_ACTIVE;
synchronized (ImfLock.class) {
mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(),
- STATE_HIDE_IME_EXPLICIT);
+ STATE_HIDE_IME_EXPLICIT, mUserId);
}
verifyHideSoftInput(true, true);
}
@@ -142,7 +147,7 @@
mInputMethodManagerService.mImeWindowVis = IME_ACTIVE;
synchronized (ImfLock.class) {
mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(),
- STATE_HIDE_IME_NOT_ALWAYS);
+ STATE_HIDE_IME_NOT_ALWAYS, mUserId);
}
verifyHideSoftInput(true, true);
}
@@ -151,7 +156,7 @@
public void testApplyImeVisibility_showImeImplicit() throws Exception {
synchronized (ImfLock.class) {
mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(),
- STATE_SHOW_IME_IMPLICIT);
+ STATE_SHOW_IME_IMPLICIT, mUserId);
}
verifyShowSoftInput(true, true, 0 /* showFlags */);
}
@@ -166,10 +171,13 @@
final var statsToken = ImeTracker.Token.empty();
synchronized (ImfLock.class) {
- final int displayIdToShowIme = mInputMethodManagerService.getDisplayIdToShowImeLocked();
+ final var bindingController =
+ mInputMethodManagerService.getInputMethodBindingController(mUserId);
+ final int displayIdToShowIme = bindingController.getDisplayIdToShowIme();
// Verify hideIme will apply the expected displayId when the default IME
// visibility applier app STATE_HIDE_IME.
- mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_HIDE_IME);
+ mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_HIDE_IME,
+ mUserId);
verify(mInputMethodManagerService.mWindowManagerInternal).hideIme(
eq(mWindowToken), eq(displayIdToShowIme), eq(statsToken));
}
@@ -204,7 +212,9 @@
// Simulate the system hides the IME when switching IME services in different users.
// (e.g. unbinding the IME from the current user to the profile user)
final var statsToken = ImeTracker.Token.empty();
- final int displayIdToShowIme = mInputMethodManagerService.getDisplayIdToShowImeLocked();
+ final var bindingController =
+ mInputMethodManagerService.getInputMethodBindingController(mUserId);
+ final int displayIdToShowIme = bindingController.getDisplayIdToShowIme();
mInputMethodManagerService.hideCurrentInputLocked(mWindowToken,
statsToken, 0 /* flags */, null /* resultReceiver */,
HIDE_SWITCH_USER);
@@ -214,7 +224,7 @@
// the IME hidden state.
// The unbind will cancel the previous stats token, and create a new one internally.
verify(mVisibilityApplier).applyImeVisibility(
- eq(mWindowToken), any(), eq(STATE_HIDE_IME));
+ eq(mWindowToken), any(), eq(STATE_HIDE_IME), eq(mUserId) /* userId */);
verify(mInputMethodManagerService.mWindowManagerInternal).hideIme(
eq(mWindowToken), eq(displayIdToShowIme), and(not(eq(statsToken)), notNull()));
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index d3efcb6..2a458c42 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -52,7 +52,6 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.server.display.brightness.clamper.BrightnessClamperController;
import com.android.server.display.config.HysteresisLevels;
import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.testutils.OffsettableClock;
@@ -102,8 +101,6 @@
@Mock Handler mNoOpHandler;
@Mock BrightnessRangeController mBrightnessRangeController;
@Mock
- BrightnessClamperController mBrightnessClamperController;
- @Mock
DisplayManagerFlags mDisplayManagerFlags;
@Mock BrightnessThrottler mBrightnessThrottler;
@@ -181,7 +178,7 @@
mContext, mBrightnessRangeController, mBrightnessThrottler,
useHorizon ? AMBIENT_LIGHT_HORIZON_SHORT : 1,
useHorizon ? AMBIENT_LIGHT_HORIZON_LONG : 10000, userLux, userNits,
- mBrightnessClamperController, mDisplayManagerFlags
+ mDisplayManagerFlags
);
when(mBrightnessRangeController.getCurrentBrightnessMax()).thenReturn(
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceInfoTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceInfoTest.java
new file mode 100644
index 0000000..bacbf89
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceInfoTest.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import static com.android.server.display.DisplayDeviceInfo.DIFF_COLOR_MODE;
+import static com.android.server.display.DisplayDeviceInfo.DIFF_COMMITTED_STATE;
+import static com.android.server.display.DisplayDeviceInfo.DIFF_HDR_SDR_RATIO;
+import static com.android.server.display.DisplayDeviceInfo.DIFF_MODE_ID;
+import static com.android.server.display.DisplayDeviceInfo.DIFF_RENDER_TIMINGS;
+import static com.android.server.display.DisplayDeviceInfo.DIFF_ROTATION;
+import static com.android.server.display.DisplayDeviceInfo.DIFF_STATE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.view.Display;
+import android.view.Surface;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DisplayDeviceInfoTest {
+
+ @Test
+ public void testDiff_noChange() {
+ var oldDdi = createInfo();
+ var newDdi = createInfo();
+
+ assertThat(oldDdi.diff(newDdi)).isEqualTo(0);
+ }
+
+ @Test
+ public void testDiff_state() {
+ var oldDdi = createInfo();
+ var newDdi = createInfo();
+
+ newDdi.state = Display.STATE_VR;
+ assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_STATE);
+ }
+
+ @Test
+ public void testDiff_committedState() {
+ var oldDdi = createInfo();
+ var newDdi = createInfo();
+
+ newDdi.committedState = Display.STATE_UNKNOWN;
+ assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_COMMITTED_STATE);
+ }
+
+ @Test
+ public void testDiff_colorMode() {
+ var oldDdi = createInfo();
+ var newDdi = createInfo();
+
+ newDdi.colorMode = Display.COLOR_MODE_DISPLAY_P3;
+ assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_COLOR_MODE);
+ }
+
+ @Test
+ public void testDiff_hdrSdrRatio() {
+ var oldDdi = createInfo();
+ var newDdi = createInfo();
+
+ /* First change new ratio to non-NaN */
+ newDdi.hdrSdrRatio = 2.3f;
+ assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_HDR_SDR_RATIO);
+
+ /* Then change old to be non-NaN and also distinct */
+ oldDdi.hdrSdrRatio = 1.1f;
+ assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_HDR_SDR_RATIO);
+
+ /* Now make the new one NaN and the old one non-NaN */
+ newDdi.hdrSdrRatio = Float.NaN;
+ assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_HDR_SDR_RATIO);
+ }
+
+ @Test
+ public void testDiff_rotation() {
+ var oldDdi = createInfo();
+ var newDdi = createInfo();
+
+ newDdi.rotation = Surface.ROTATION_270;
+ assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_ROTATION);
+ }
+
+ @Test
+ public void testDiff_frameRate() {
+ var oldDdi = createInfo();
+ var newDdi = createInfo();
+
+ newDdi.renderFrameRate = 123.4f;
+ assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_RENDER_TIMINGS);
+ newDdi.renderFrameRate = oldDdi.renderFrameRate;
+
+ newDdi.appVsyncOffsetNanos = 31222221;
+ assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_RENDER_TIMINGS);
+ newDdi.appVsyncOffsetNanos = oldDdi.appVsyncOffsetNanos;
+
+ newDdi.presentationDeadlineNanos = 23000000;
+ assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_RENDER_TIMINGS);
+ }
+
+ @Test
+ public void testDiff_modeId() {
+ var oldDdi = createInfo();
+ var newDdi = createInfo();
+
+ newDdi.modeId = 9;
+ assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_MODE_ID);
+ }
+
+ private static DisplayDeviceInfo createInfo() {
+ var ddi = new DisplayDeviceInfo();
+ ddi.name = "TestDisplayDeviceInfo";
+ ddi.uniqueId = "test:51651561321";
+ ddi.width = 671;
+ ddi.height = 483;
+ ddi.modeId = 2;
+ ddi.renderFrameRate = 68.9f;
+ ddi.supportedModes = new Display.Mode[] {
+ new Display.Mode.Builder().setRefreshRate(68.9f).setResolution(671, 483).build(),
+ };
+ ddi.appVsyncOffsetNanos = 6233332;
+ ddi.presentationDeadlineNanos = 11500000;
+ ddi.rotation = Surface.ROTATION_90;
+ ddi.state = Display.STATE_ON;
+ ddi.committedState = Display.STATE_DOZE_SUSPEND;
+ return ddi;
+ }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index d0eb83a..211ab03 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -2569,6 +2569,63 @@
}
@Test
+ public void testPowerOnAndOffInternalDisplay() {
+ manageDisplaysPermission(/* granted= */ true);
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService bs = displayManager.new BinderService();
+ LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
+ FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
+ bs.registerCallbackWithEventMask(callback, STANDARD_AND_CONNECTION_DISPLAY_EVENTS);
+
+ callback.expectsEvent(EVENT_DISPLAY_ADDED);
+ FakeDisplayDevice displayDevice =
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL);
+ callback.waitForExpectedEvent();
+
+ LogicalDisplay display =
+ logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true);
+
+ assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState)
+ .isEqualTo(Display.STATE_ON);
+
+ assertThat(displayManager.requestDisplayPower(display.getDisplayIdLocked(), false))
+ .isTrue();
+
+ assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState)
+ .isEqualTo(Display.STATE_OFF);
+
+ assertThat(displayManager.requestDisplayPower(display.getDisplayIdLocked(), true))
+ .isTrue();
+
+ assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState)
+ .isEqualTo(Display.STATE_ON);
+ }
+
+ @Test
+ public void testPowerOnAndOffInternalDisplay_withoutPermission_shouldThrowException() {
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService bs = displayManager.new BinderService();
+ LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
+ FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
+ bs.registerCallbackWithEventMask(callback, STANDARD_AND_CONNECTION_DISPLAY_EVENTS);
+
+ callback.expectsEvent(EVENT_DISPLAY_ADDED);
+ FakeDisplayDevice displayDevice =
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL);
+ callback.waitForExpectedEvent();
+
+ LogicalDisplay display =
+ logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true);
+ var displayId = display.getDisplayIdLocked();
+
+ assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState)
+ .isEqualTo(Display.STATE_ON);
+
+ assertThrows(SecurityException.class, () -> bs.requestDisplayPower(displayId, true));
+ assertThrows(SecurityException.class, () -> bs.requestDisplayPower(displayId, false));
+ }
+
+ @Test
public void testEnableExternalDisplay_withDisplayManagement_shouldSignalDisplayAdded() {
when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
manageDisplaysPermission(/* granted= */ true);
@@ -3529,6 +3586,7 @@
public void setDisplayDeviceInfo(DisplayDeviceInfo displayDeviceInfo) {
mDisplayDeviceInfo = displayDeviceInfo;
+ mDisplayDeviceInfo.committedState = Display.STATE_ON;
}
@Override
@@ -3558,5 +3616,14 @@
public Display.Mode getUserPreferredDisplayModeLocked() {
return mPreferredMode;
}
+
+ @Override
+ public Runnable requestDisplayStateLocked(
+ final int state,
+ final float brightnessState,
+ final float sdrBrightnessState,
+ @Nullable DisplayOffloadSessionImpl displayOffloadSession) {
+ return () -> mDisplayDeviceInfo.committedState = state;
+ }
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index efa224f..95f0b65 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -1415,7 +1415,7 @@
when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f);
when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f);
- when(mHolder.clamperController.clamp(any(), anyFloat(), anyBoolean())).thenAnswer(
+ when(mHolder.clamperController.clamp(any(), anyFloat(), anyBoolean(), anyInt())).thenAnswer(
invocation -> DisplayBrightnessState.builder()
.setIsSlowChange(invocation.getArgument(2))
.setBrightness(invocation.getArgument(1))
@@ -1439,7 +1439,7 @@
when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f);
when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f);
- when(mHolder.clamperController.clamp(any(), anyFloat(), anyBoolean())).thenAnswer(
+ when(mHolder.clamperController.clamp(any(), anyFloat(), anyBoolean(), anyInt())).thenAnswer(
invocation -> DisplayBrightnessState.builder()
.setIsSlowChange(invocation.getArgument(2))
.setBrightness(invocation.getArgument(1))
@@ -2094,7 +2094,7 @@
BrightnessClamperController clamperController = mock(BrightnessClamperController.class);
when(hbmController.getCurrentBrightnessMax()).thenReturn(PowerManager.BRIGHTNESS_MAX);
- when(clamperController.clamp(any(), anyFloat(), anyBoolean())).thenAnswer(
+ when(clamperController.clamp(any(), anyFloat(), anyBoolean(), anyInt())).thenAnswer(
invocation -> DisplayBrightnessState.builder()
.setIsSlowChange(invocation.getArgument(2))
.setBrightness(invocation.getArgument(1))
@@ -2328,7 +2328,7 @@
BrightnessClamperController getBrightnessClamperController(Handler handler,
BrightnessClamperController.ClamperChangeListener clamperChangeListener,
BrightnessClamperController.DisplayDeviceData data, Context context,
- DisplayManagerFlags flags) {
+ DisplayManagerFlags flags, SensorManager sensorManager) {
return mClamperController;
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
index 5487bc5..69043f5 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
@@ -16,14 +16,20 @@
package com.android.server.display.brightness.clamper;
+import static android.view.Display.STATE_ON;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
import android.hardware.display.BrightnessInfo;
import android.hardware.display.DisplayManagerInternal;
import android.os.Handler;
@@ -34,11 +40,15 @@
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.internal.util.test.FakeSettingsProviderRule;
import com.android.server.display.DisplayBrightnessState;
import com.android.server.display.DisplayDeviceConfig;
+import com.android.server.display.TestUtils;
import com.android.server.display.brightness.BrightnessReason;
import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.display.feature.DisplayManagerFlags;
+import com.android.server.testutils.OffsettableClock;
import com.android.server.testutils.TestHandler;
import org.junit.Before;
@@ -54,13 +64,16 @@
public class BrightnessClamperControllerTest {
private static final float FLOAT_TOLERANCE = 0.001f;
- private final TestHandler mTestHandler = new TestHandler(null);
+ private final OffsettableClock mClock = new OffsettableClock();
+ private final TestHandler mTestHandler = new TestHandler(null, mClock);
@Rule
public final TestableContext mMockContext = new TestableContext(
InstrumentationRegistry.getInstrumentation().getContext());
@Mock
private BrightnessClamperController.ClamperChangeListener mMockExternalListener;
+ @Mock
+ private SensorManager mSensorManager;
@Mock
private BrightnessClamperController.DisplayDeviceData mMockDisplayDeviceData;
@@ -74,15 +87,25 @@
private BrightnessModifier mMockModifier;
@Mock
private DisplayManagerInternal.DisplayPowerRequest mMockRequest;
+
+ Sensor mLightSensor;
+
@Mock
private DeviceConfig.Properties mMockProperties;
private BrightnessClamperController mClamperController;
private TestInjector mTestInjector;
+ @Rule
+ public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
+
@Before
- public void setUp() {
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ mLightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor");
mTestInjector = new TestInjector(List.of(mMockClamper), List.of(mMockModifier));
+ when(mSensorManager.getDefaultSensor(anyInt())).thenReturn(mLightSensor);
+ when(mMockModifier.shouldListenToLightSensor()).thenReturn(true);
+
mClamperController = createBrightnessClamperController();
}
@@ -132,7 +155,7 @@
public void testClamp_AppliesModifier() {
float initialBrightness = 0.2f;
boolean initialSlowChange = true;
- mClamperController.clamp(mMockRequest, initialBrightness, initialSlowChange);
+ mClamperController.clamp(mMockRequest, initialBrightness, initialSlowChange, STATE_ON);
verify(mMockModifier).apply(eq(mMockRequest), any());
}
@@ -151,7 +174,7 @@
mTestHandler.flush();
DisplayBrightnessState state = mClamperController.clamp(mMockRequest, initialBrightness,
- initialSlowChange);
+ initialSlowChange, STATE_ON);
assertEquals(initialBrightness, state.getBrightness(), FLOAT_TOLERANCE);
assertEquals(PowerManager.BRIGHTNESS_MAX, state.getMaxBrightness(), FLOAT_TOLERANCE);
@@ -175,7 +198,7 @@
mTestHandler.flush();
DisplayBrightnessState state = mClamperController.clamp(mMockRequest, initialBrightness,
- initialSlowChange);
+ initialSlowChange, STATE_ON);
assertEquals(clampedBrightness, state.getBrightness(), FLOAT_TOLERANCE);
assertEquals(clampedBrightness, state.getMaxBrightness(), FLOAT_TOLERANCE);
@@ -199,7 +222,7 @@
mTestHandler.flush();
DisplayBrightnessState state = mClamperController.clamp(mMockRequest, initialBrightness,
- initialSlowChange);
+ initialSlowChange, STATE_ON);
assertEquals(initialBrightness, state.getBrightness(), FLOAT_TOLERANCE);
assertEquals(clampedBrightness, state.getMaxBrightness(), FLOAT_TOLERANCE);
@@ -223,10 +246,10 @@
mTestHandler.flush();
// first call of clamp method
mClamperController.clamp(mMockRequest, initialBrightness,
- initialSlowChange);
+ initialSlowChange, STATE_ON);
// immediately second call of clamp method
DisplayBrightnessState state = mClamperController.clamp(mMockRequest, initialBrightness,
- initialSlowChange);
+ initialSlowChange, STATE_ON);
assertEquals(clampedBrightness, state.getBrightness(), FLOAT_TOLERANCE);
assertEquals(clampedBrightness, state.getMaxBrightness(), FLOAT_TOLERANCE);
@@ -237,6 +260,31 @@
}
@Test
+ public void testAmbientLuxChanges() throws Exception {
+ ArgumentCaptor<SensorEventListener> listenerCaptor = ArgumentCaptor.forClass(
+ SensorEventListener.class);
+
+ verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+ anyInt(), any(Handler.class));
+ SensorEventListener listener = listenerCaptor.getValue();
+
+ when(mSensorManager.getSensorList(eq(Sensor.TYPE_ALL))).thenReturn(List.of(mLightSensor));
+
+ float initialBrightness = 0.8f;
+ boolean initialSlowChange = true;
+
+ DisplayBrightnessState state = mClamperController.clamp(mMockRequest, initialBrightness,
+ initialSlowChange, STATE_ON);
+ assertEquals(initialBrightness, state.getBrightness(), FLOAT_TOLERANCE);
+
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 50, mClock.now()));
+ verify(mMockModifier).setAmbientLux(50);
+
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 300, mClock.now()));
+ verify(mMockModifier).setAmbientLux(300);
+ }
+
+ @Test
public void testStop() {
mClamperController.stop();
verify(mMockModifier).stop();
@@ -245,7 +293,7 @@
private BrightnessClamperController createBrightnessClamperController() {
return new BrightnessClamperController(mTestInjector, mTestHandler, mMockExternalListener,
- mMockDisplayDeviceData, mMockContext, mFlags);
+ mMockDisplayDeviceData, mMockContext, mFlags, mSensorManager);
}
private class TestInjector extends BrightnessClamperController.Injector {
@@ -282,7 +330,7 @@
@Override
List<BrightnessStateModifier> getModifiers(DisplayManagerFlags flags, Context context,
Handler handler, BrightnessClamperController.ClamperChangeListener listener,
- DisplayDeviceConfig displayDeviceConfig) {
+ DisplayDeviceConfig displayDeviceConfig, SensorManager sensorManager) {
return mModifiers;
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowLuxModifierTest.kt b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowLuxModifierTest.kt
index 21e066d..d672435 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowLuxModifierTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowLuxModifierTest.kt
@@ -20,8 +20,6 @@
import android.provider.Settings
import android.testing.TestableContext
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED
-import com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED
import com.android.server.display.DisplayDeviceConfig
import com.android.server.display.brightness.BrightnessReason
import com.android.server.display.feature.flags.Flags
@@ -54,10 +52,12 @@
@Before
fun setUp() {
modifier =
- BrightnessLowLuxModifier(testHandler,
+ BrightnessLowLuxModifier(
+ testHandler,
mockClamperChangeListener,
context,
- mockDisplayDeviceConfig)
+ mockDisplayDeviceConfig
+ )
// values below transition point (even dimmer range)
// nits: 0.1 -> backlight 0.02 -> brightness -> 0.1
@@ -66,7 +66,7 @@
whenever(mockDisplayDeviceConfig.getBrightnessFromBacklight(/* backlight = */ 0.02f))
.thenReturn(LOW_LUX_BRIGHTNESS)
- // values above transition point (noraml range)
+ // values above transition point (normal range)
// nits: 10 -> backlight 0.2 -> brightness -> 0.3
whenever(mockDisplayDeviceConfig.getBacklightFromNits(/* nits= */ 2f))
.thenReturn(0.15f)
@@ -95,12 +95,12 @@
// test transition point ensures brightness doesn't drop when setting is off.
Settings.Secure.putIntForUser(context.contentResolver,
Settings.Secure.EVEN_DIMMER_ACTIVATED, 0, USER_ID)
- modifier.setAutoBrightnessState(AUTO_BRIGHTNESS_ENABLED)
modifier.recalculateLowerBound()
testHandler.flush()
assertThat(modifier.brightnessLowerBound).isEqualTo(TRANSITION_POINT)
assertThat(modifier.brightnessReason).isEqualTo(0) // no reason - ie off
- modifier.onAmbientLuxChange(3000.0f)
+ modifier.setAmbientLux(3000f)
+
testHandler.flush()
assertThat(modifier.isActive).isFalse()
assertThat(modifier.brightnessLowerBound).isEqualTo(TRANSITION_POINT)
@@ -115,8 +115,8 @@
Settings.Secure.EVEN_DIMMER_ACTIVATED, 1, USER_ID)
Settings.Secure.putFloatForUser(context.contentResolver,
Settings.Secure.EVEN_DIMMER_MIN_NITS, 0.1f, USER_ID)
- modifier.setAutoBrightnessState(AUTO_BRIGHTNESS_ENABLED)
- modifier.onAmbientLuxChange(400.0f)
+ modifier.setAmbientLux(400f)
+
testHandler.flush()
assertThat(modifier.isActive).isTrue()
@@ -133,7 +133,6 @@
Settings.Secure.EVEN_DIMMER_ACTIVATED, 1, USER_ID)
Settings.Secure.putFloatForUser(context.contentResolver,
Settings.Secure.EVEN_DIMMER_MIN_NITS, 10.0f, USER_ID)
- modifier.setAutoBrightnessState(AUTO_BRIGHTNESS_ENABLED)
modifier.recalculateLowerBound()
testHandler.flush()
@@ -152,8 +151,8 @@
Settings.Secure.EVEN_DIMMER_ACTIVATED, 1, USER_ID) // on
Settings.Secure.putFloatForUser(context.contentResolver,
Settings.Secure.EVEN_DIMMER_MIN_NITS, 1.0f, USER_ID)
- modifier.setAutoBrightnessState(AUTO_BRIGHTNESS_ENABLED)
- modifier.onAmbientLuxChange(400.0f)
+ modifier.setAmbientLux(400f)
+
testHandler.flush()
assertThat(modifier.isActive).isTrue()
@@ -180,8 +179,8 @@
Settings.Secure.EVEN_DIMMER_ACTIVATED, 0, USER_ID) // off
Settings.Secure.putFloatForUser(context.contentResolver,
Settings.Secure.EVEN_DIMMER_MIN_NITS, 1.0f, USER_ID)
- modifier.setAutoBrightnessState(AUTO_BRIGHTNESS_ENABLED)
- modifier.onAmbientLuxChange(400.0f)
+ modifier.setAmbientLux(400f)
+
testHandler.flush()
assertThat(modifier.isActive).isFalse()
@@ -203,15 +202,14 @@
@Test
@RequiresFlagsEnabled(Flags.FLAG_EVEN_DIMMER)
- fun testDisabledWhenAutobrightnessIsOff() {
+ fun testEnabledEvenWhenAutobrightnessIsOff() {
// test that high lux prevents low brightness range.
Settings.Secure.putIntForUser(context.contentResolver,
Settings.Secure.EVEN_DIMMER_ACTIVATED, 1, USER_ID) // on
Settings.Secure.putFloatForUser(context.contentResolver,
Settings.Secure.EVEN_DIMMER_MIN_NITS, 1.0f, USER_ID)
- modifier.setAutoBrightnessState(AUTO_BRIGHTNESS_ENABLED)
- modifier.onAmbientLuxChange(400.0f)
+ modifier.setAmbientLux(400f)
testHandler.flush()
assertThat(modifier.isActive).isTrue()
@@ -219,15 +217,13 @@
assertThat(modifier.brightnessReason).isEqualTo(BrightnessReason.MODIFIER_MIN_LUX)
assertThat(modifier.brightnessLowerBound).isEqualTo(LOW_LUX_BRIGHTNESS)
-
- modifier.setAutoBrightnessState(AUTO_BRIGHTNESS_DISABLED)
- modifier.onAmbientLuxChange(400.0f)
+ modifier.setAmbientLux(400f)
testHandler.flush()
- assertThat(modifier.isActive).isFalse()
+ assertThat(modifier.isActive).isTrue()
// Test restriction from lux setting
- assertThat(modifier.brightnessReason).isEqualTo(0)
- assertThat(modifier.brightnessLowerBound).isEqualTo(TRANSITION_POINT)
+ assertThat(modifier.brightnessReason).isEqualTo(BrightnessReason.MODIFIER_MIN_LUX)
+ assertThat(modifier.brightnessLowerBound).isEqualTo(LOW_LUX_BRIGHTNESS)
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
index 8a33f34..1d04baa 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
@@ -570,6 +570,86 @@
assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState);
}
+ @Test
+ public void
+ updateBrightness_constructsDisplayBrightnessState_withNoAdjustmentFlag_isSlowChange() {
+ BrightnessEvent brightnessEvent = new BrightnessEvent(DISPLAY_ID);
+ mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy(
+ mContext, DISPLAY_ID, displayId -> brightnessEvent, mDisplayManagerFlags);
+ mAutomaticBrightnessStrategy.setAutomaticBrightnessController(
+ mAutomaticBrightnessController);
+ float brightness = 0.4f;
+ BrightnessReason brightnessReason = new BrightnessReason();
+ brightnessReason.setReason(BrightnessReason.REASON_AUTOMATIC);
+ when(mAutomaticBrightnessController.getAutomaticScreenBrightness(brightnessEvent))
+ .thenReturn(brightness);
+
+ // Set the state such that auto-brightness was already applied
+ mAutomaticBrightnessStrategy.setAutoBrightnessApplied(true);
+
+ // Update the auto-brightess validity state to change the isSlowChange flag
+ mAutomaticBrightnessStrategy.isAutoBrightnessValid();
+
+ DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
+ mock(DisplayManagerInternal.DisplayPowerRequest.class);
+
+ DisplayBrightnessState expectedDisplayBrightnessState = new DisplayBrightnessState.Builder()
+ .setBrightness(brightness)
+ .setSdrBrightness(brightness)
+ .setBrightnessReason(brightnessReason)
+ .setDisplayBrightnessStrategyName(mAutomaticBrightnessStrategy.getName())
+ .setIsSlowChange(true)
+ .setBrightnessEvent(brightnessEvent)
+ .setBrightnessAdjustmentFlag(0)
+ .setShouldUpdateScreenBrightnessSetting(true)
+ .setIsUserInitiatedChange(true)
+ .build();
+ DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy
+ .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f,
+ /* userSetBrightnessChanged= */ true));
+ assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState);
+ }
+
+
+ @Test
+ public void updateBrightness_autoBrightnessNotApplied_noAdjustments_isNotSlowChange() {
+ BrightnessEvent brightnessEvent = new BrightnessEvent(DISPLAY_ID);
+ mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy(
+ mContext, DISPLAY_ID, displayId -> brightnessEvent, mDisplayManagerFlags);
+ mAutomaticBrightnessStrategy.setAutomaticBrightnessController(
+ mAutomaticBrightnessController);
+ float brightness = 0.4f;
+ BrightnessReason brightnessReason = new BrightnessReason();
+ brightnessReason.setReason(BrightnessReason.REASON_AUTOMATIC);
+ when(mAutomaticBrightnessController.getAutomaticScreenBrightness(brightnessEvent))
+ .thenReturn(brightness);
+
+ // Set the state such that auto-brightness was not already applied
+ mAutomaticBrightnessStrategy.setAutoBrightnessApplied(false);
+
+ // Update the auto-brightess validity state to change the isSlowChange flag
+ mAutomaticBrightnessStrategy.isAutoBrightnessValid();
+
+ DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
+ mock(DisplayManagerInternal.DisplayPowerRequest.class);
+
+ DisplayBrightnessState expectedDisplayBrightnessState = new DisplayBrightnessState.Builder()
+ .setBrightness(brightness)
+ .setSdrBrightness(brightness)
+ .setBrightnessReason(brightnessReason)
+ .setDisplayBrightnessStrategyName(mAutomaticBrightnessStrategy.getName())
+ .setIsSlowChange(false)
+ .setBrightnessEvent(brightnessEvent)
+ .setBrightnessAdjustmentFlag(0)
+ .setShouldUpdateScreenBrightnessSetting(true)
+ .setIsUserInitiatedChange(true)
+ .build();
+ DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy
+ .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f,
+ /* userSetBrightnessChanged= */ true));
+ assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState);
+ }
+
private void setPendingAutoBrightnessAdjustment(float pendingAutoBrightnessAdjustment) {
Settings.System.putFloat(mContext.getContentResolver(),
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, pendingAutoBrightnessAdjustment);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
index e88e28b..ee96c2a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
@@ -74,6 +74,15 @@
private static final String TAG = ApplicationStartInfoTest.class.getSimpleName();
private static final ComponentName COMPONENT = new ComponentName("com.android.test", ".Foo");
+ private static final int APP_1_UID = 10123;
+ private static final int APP_1_PID_1 = 12345;
+ private static final int APP_1_PID_2 = 12346;
+ private static final int APP_1_DEFINING_UID = 23456;
+ private static final int APP_1_UID_USER_2 = 1010123;
+ private static final int APP_1_PID_USER_2 = 12347;
+ private static final String APP_1_PROCESS_NAME = "com.android.test.stub1:process";
+ private static final String APP_1_PACKAGE_NAME = "com.android.test.stub1";
+
@Rule public ServiceThreadRule mServiceThreadRule = new ServiceThreadRule();
@Mock private AppOpsService mAppOpsService;
@Mock private PackageManagerInternal mPackageManagerInt;
@@ -111,6 +120,12 @@
// Remove stale instance of PackageManagerInternal if there is any
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt);
+
+ mAppStartInfoTracker.clearProcessStartInfo(true);
+ mAppStartInfoTracker.mAppStartInfoLoaded.set(true);
+ mAppStartInfoTracker.mAppStartInfoHistoryListSize =
+ mAppStartInfoTracker.APP_START_INFO_HISTORY_LIST_SIZE;
+ doNothing().when(mAppStartInfoTracker).schedulePersistProcessStartInfo(anyBoolean());
}
@After
@@ -120,26 +135,12 @@
@Test
public void testApplicationStartInfo() throws Exception {
- mAppStartInfoTracker.clearProcessStartInfo(true);
- mAppStartInfoTracker.mAppStartInfoLoaded.set(true);
- mAppStartInfoTracker.mAppStartInfoHistoryListSize =
- mAppStartInfoTracker.APP_START_INFO_HISTORY_LIST_SIZE;
mAppStartInfoTracker.mProcStartStoreDir = new File(mContext.getFilesDir(),
AppStartInfoTracker.APP_START_STORE_DIR);
assertTrue(FileUtils.createDir(mAppStartInfoTracker.mProcStartStoreDir));
mAppStartInfoTracker.mProcStartInfoFile = new File(mAppStartInfoTracker.mProcStartStoreDir,
AppStartInfoTracker.APP_START_INFO_FILE);
- doNothing().when(mAppStartInfoTracker).schedulePersistProcessStartInfo(anyBoolean());
-
- final int app1Uid = 10123;
- final int app1Pid1 = 12345;
- final int app1Pid2 = 12346;
- final int app1DefiningUid = 23456;
- final int app1UidUser2 = 1010123;
- final int app1PidUser2 = 12347;
- final String app1ProcessName = "com.android.test.stub1:process";
- final String app1PackageName = "com.android.test.stub1";
final long appStartTimestampIntentStarted = 1000000;
final long appStartTimestampActivityLaunchFinished = 2000000;
final long appStartTimestampFirstFrameDrawn = 2500000;
@@ -149,23 +150,23 @@
final long appStartTimestampRContentProvider = 6000000;
ProcessRecord app = makeProcessRecord(
- app1Pid1, // pid
- app1Uid, // uid
- app1Uid, // packageUid
+ APP_1_PID_1, // pid
+ APP_1_UID, // uid
+ APP_1_UID, // packageUid
null, // definingUid
- app1ProcessName, // processName
- app1PackageName); // packageName
+ APP_1_PROCESS_NAME, // processName
+ APP_1_PACKAGE_NAME); // packageName
ArrayList<ApplicationStartInfo> list = new ArrayList<ApplicationStartInfo>();
// Case 1: Activity start intent failed
mAppStartInfoTracker.onIntentStarted(buildIntent(COMPONENT),
appStartTimestampIntentStarted);
- mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list);
+ mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list);
verifyInProgressRecordsSize(1);
assertEquals(list.size(), 0);
- verifyInProgApplicationStartInfo(
+ verifyInProgressApplicationStartInfo(
0, // index
0, // pid
0, // uid
@@ -179,7 +180,7 @@
mAppStartInfoTracker.onIntentFailed(appStartTimestampIntentStarted);
list.clear();
- mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list);
+ mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list);
verifyInProgressRecordsSize(0);
assertEquals(list.size(), 0);
@@ -189,24 +190,24 @@
mAppStartInfoTracker.onIntentStarted(buildIntent(COMPONENT),
appStartTimestampIntentStarted);
list.clear();
- mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list);
+ mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list);
verifyInProgressRecordsSize(1);
assertEquals(list.size(), 0);
mAppStartInfoTracker.onActivityLaunched(appStartTimestampIntentStarted, COMPONENT,
ApplicationStartInfo.START_TYPE_COLD, app);
list.clear();
- mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list);
+ mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list);
verifyInProgressRecordsSize(1);
assertEquals(list.size(), 1);
- verifyInProgApplicationStartInfo(
+ verifyInProgressApplicationStartInfo(
0, // index
- app1Pid1, // pid
- app1Uid, // uid
- app1Uid, // packageUid
+ APP_1_PID_1, // pid
+ APP_1_UID, // uid
+ APP_1_UID, // packageUid
null, // definingUid
- app1ProcessName, // processName
+ APP_1_PROCESS_NAME, // processName
ApplicationStartInfo.START_REASON_START_ACTIVITY, // reason
ApplicationStartInfo.STARTUP_STATE_STARTED, // startup state
ApplicationStartInfo.START_TYPE_COLD, // state type
@@ -214,17 +215,17 @@
mAppStartInfoTracker.onActivityLaunchCancelled(appStartTimestampIntentStarted);
list.clear();
- mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list);
+ mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list);
verifyInProgressRecordsSize(0);
assertEquals(list.size(), 1);
verifyApplicationStartInfo(
list.get(0), // info
- app1Pid1, // pid
- app1Uid, // uid
- app1Uid, // packageUid
+ APP_1_PID_1, // pid
+ APP_1_UID, // uid
+ APP_1_UID, // packageUid
null, // definingUid
- app1ProcessName, // processName
+ APP_1_PROCESS_NAME, // processName
ApplicationStartInfo.START_REASON_START_ACTIVITY, // reason
ApplicationStartInfo.STARTUP_STATE_ERROR, // startup state
ApplicationStartInfo.START_TYPE_COLD, // state type
@@ -236,24 +237,24 @@
mAppStartInfoTracker.onIntentStarted(buildIntent(COMPONENT),
appStartTimestampIntentStarted);
list.clear();
- mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list);
+ mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list);
verifyInProgressRecordsSize(1);
assertEquals(list.size(), 0);
mAppStartInfoTracker.onActivityLaunched(appStartTimestampIntentStarted, COMPONENT,
ApplicationStartInfo.START_TYPE_COLD, app);
list.clear();
- mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list);
+ mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list);
verifyInProgressRecordsSize(1);
assertEquals(list.size(), 1);
- verifyInProgApplicationStartInfo(
+ verifyInProgressApplicationStartInfo(
0, // index
- app1Pid1, // pid
- app1Uid, // uid
- app1Uid, // packageUid
+ APP_1_PID_1, // pid
+ APP_1_UID, // uid
+ APP_1_UID, // packageUid
null, // definingUid
- app1ProcessName, // processName
+ APP_1_PROCESS_NAME, // processName
ApplicationStartInfo.START_REASON_START_ACTIVITY, // reason
ApplicationStartInfo.STARTUP_STATE_STARTED, // startup state
ApplicationStartInfo.START_TYPE_COLD, // state type
@@ -261,11 +262,11 @@
verifyApplicationStartInfo(
list.get(0), // info
- app1Pid1, // pid
- app1Uid, // uid
- app1Uid, // packageUid
+ APP_1_PID_1, // pid
+ APP_1_UID, // uid
+ APP_1_UID, // packageUid
null, // definingUid
- app1ProcessName, // processName
+ APP_1_PROCESS_NAME, // processName
ApplicationStartInfo.START_REASON_START_ACTIVITY, // reason
ApplicationStartInfo.STARTUP_STATE_STARTED, // startup state
ApplicationStartInfo.START_TYPE_COLD, // state type
@@ -273,20 +274,20 @@
mAppStartInfoTracker.onActivityLaunchFinished(appStartTimestampIntentStarted, COMPONENT,
appStartTimestampActivityLaunchFinished, ApplicationStartInfo.LAUNCH_MODE_STANDARD);
- mAppStartInfoTracker.addTimestampToStart(app1PackageName, app1Uid,
+ mAppStartInfoTracker.addTimestampToStart(APP_1_PACKAGE_NAME, APP_1_UID,
appStartTimestampFirstFrameDrawn, ApplicationStartInfo.START_TIMESTAMP_FIRST_FRAME);
list.clear();
- mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list);
+ mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list);
verifyInProgressRecordsSize(1);
assertEquals(list.size(), 1);
- verifyInProgApplicationStartInfo(
+ verifyInProgressApplicationStartInfo(
0, // index
- app1Pid1, // pid
- app1Uid, // uid
- app1Uid, // packageUid
+ APP_1_PID_1, // pid
+ APP_1_UID, // uid
+ APP_1_UID, // packageUid
null, // definingUid
- app1ProcessName, // processName
+ APP_1_PROCESS_NAME, // processName
ApplicationStartInfo.START_REASON_START_ACTIVITY, // reason
ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN, // startup state
ApplicationStartInfo.START_TYPE_COLD, // state type
@@ -295,17 +296,17 @@
mAppStartInfoTracker.onReportFullyDrawn(appStartTimestampIntentStarted,
appStartTimestampReportFullyDrawn);
list.clear();
- mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list);
+ mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list);
verifyInProgressRecordsSize(0);
assertEquals(list.size(), 1);
verifyApplicationStartInfo(
list.get(0), // info
- app1Pid1, // pid
- app1Uid, // uid
- app1Uid, // packageUid
+ APP_1_PID_1, // pid
+ APP_1_UID, // uid
+ APP_1_UID, // packageUid
null, // definingUid
- app1ProcessName, // processName
+ APP_1_PROCESS_NAME, // processName
ApplicationStartInfo.START_REASON_START_ACTIVITY, // reason
ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN, // startup state
ApplicationStartInfo.START_TYPE_COLD, // state type
@@ -316,26 +317,26 @@
// Case 4: Create an other app1 record with different pid started for a service
sleep(1);
app = makeProcessRecord(
- app1Pid2, // pid
- app1Uid, // uid
- app1Uid, // packageUid
- app1DefiningUid, // definingUid
- app1ProcessName, // processName
- app1PackageName); // packageName
+ APP_1_PID_2, // pid
+ APP_1_UID, // uid
+ APP_1_UID, // packageUid
+ APP_1_DEFINING_UID, // definingUid
+ APP_1_PROCESS_NAME, // processName
+ APP_1_PACKAGE_NAME); // packageName
ServiceRecord service = ServiceRecord.newEmptyInstanceForTest(mAms);
mAppStartInfoTracker.handleProcessServiceStart(appStartTimestampService, app, service);
list.clear();
- mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, 0, 0, list);
+ mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, 0, 0, list);
assertEquals(list.size(), 2);
verifyApplicationStartInfo(
list.get(0), // info
- app1Pid2, // pid
- app1Uid, // uid
- app1Uid, // packageUid
- app1DefiningUid, // definingUid
- app1ProcessName, // processName
+ APP_1_PID_2, // pid
+ APP_1_UID, // uid
+ APP_1_UID, // packageUid
+ APP_1_DEFINING_UID, // definingUid
+ APP_1_PROCESS_NAME, // processName
ApplicationStartInfo.START_REASON_SERVICE, // reason
ApplicationStartInfo.STARTUP_STATE_STARTED, // startup state
ApplicationStartInfo.START_TYPE_COLD, // state type
@@ -344,39 +345,41 @@
// Case 5: Create an instance of app1 with a different user started for a broadcast
sleep(1);
app = makeProcessRecord(
- app1PidUser2, // pid
- app1UidUser2, // uid
- app1UidUser2, // packageUid
+ APP_1_PID_USER_2, // pid
+ APP_1_UID_USER_2, // uid
+ APP_1_UID_USER_2, // packageUid
null, // definingUid
- app1ProcessName, // processName
- app1PackageName); // packageName
+ APP_1_PROCESS_NAME, // processName
+ APP_1_PACKAGE_NAME); // packageName
mAppStartInfoTracker.handleProcessBroadcastStart(appStartTimestampBroadcast, app,
buildIntent(COMPONENT), false /* isAlarm */);
list.clear();
- mAppStartInfoTracker.getStartInfo(app1PackageName, app1UidUser2, app1PidUser2, 0, list);
+ mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID_USER_2, APP_1_PID_USER_2, 0,
+ list);
assertEquals(list.size(), 1);
verifyApplicationStartInfo(
list.get(0), // info
- app1PidUser2, // pid
- app1UidUser2, // uid
- app1UidUser2, // packageUid
+ APP_1_PID_USER_2, // pid
+ APP_1_UID_USER_2, // uid
+ APP_1_UID_USER_2, // packageUid
null, // definingUid
- app1ProcessName, // processName
+ APP_1_PROCESS_NAME, // processName
ApplicationStartInfo.START_REASON_BROADCAST, // reason
ApplicationStartInfo.STARTUP_STATE_STARTED, // startup state
ApplicationStartInfo.START_TYPE_COLD, // state type
ApplicationStartInfo.LAUNCH_MODE_STANDARD); // launch mode
// Case 6: User 2 gets removed
- mAppStartInfoTracker.onPackageRemoved(app1PackageName, app1UidUser2, false);
+ mAppStartInfoTracker.onPackageRemoved(APP_1_PACKAGE_NAME, APP_1_UID_USER_2, false);
list.clear();
- mAppStartInfoTracker.getStartInfo(app1PackageName, app1UidUser2, app1PidUser2, 0, list);
+ mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID_USER_2, APP_1_PID_USER_2, 0,
+ list);
assertEquals(list.size(), 0);
list.clear();
- mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1PidUser2, 0, list);
+ mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_USER_2, 0, list);
assertEquals(list.size(), 2);
@@ -416,7 +419,7 @@
// Case 8: Save and load again
ArrayList<ApplicationStartInfo> original = new ArrayList<ApplicationStartInfo>();
- mAppStartInfoTracker.getStartInfo(null, app1Uid, 0, 0, original);
+ mAppStartInfoTracker.getStartInfo(null, APP_1_UID, 0, 0, original);
assertTrue(original.size() > 0);
mAppStartInfoTracker.persistProcessStartInfo();
@@ -424,12 +427,12 @@
mAppStartInfoTracker.clearProcessStartInfo(false);
list.clear();
- mAppStartInfoTracker.getStartInfo(null, app1Uid, 0, 0, list);
+ mAppStartInfoTracker.getStartInfo(null, APP_1_UID, 0, 0, list);
assertEquals(0, list.size());
mAppStartInfoTracker.loadExistingProcessStartInfo();
list.clear();
- mAppStartInfoTracker.getStartInfo(null, app1Uid, 0, 0, list);
+ mAppStartInfoTracker.getStartInfo(null, APP_1_UID, 0, 0, list);
assertEquals(original.size(), list.size());
for (int i = list.size() - 1; i >= 0; i--) {
@@ -437,6 +440,48 @@
}
}
+ /**
+ * Test to make sure that in progress records stay within their size limits and discard the
+ * correct records.
+ */
+ @SuppressWarnings("GuardedBy")
+ @Test
+ public void testInProgressRecordsLimit() throws Exception {
+ ProcessRecord app = makeProcessRecord(
+ APP_1_PID_1, // pid
+ APP_1_UID, // uid
+ APP_1_UID, // packageUid
+ null, // definingUid
+ APP_1_PROCESS_NAME, // processName
+ APP_1_PACKAGE_NAME); // packageName
+
+ // Mock performing 2 x MAX_IN_PROGRESS_RECORDS successful starts and ensure that the list
+ // never exceeds the expected size of MAX_IN_PROGRESS_RECORDS.
+ for (int i = 0; i < AppStartInfoTracker.MAX_IN_PROGRESS_RECORDS * 2; i++) {
+ Long startTime = Long.valueOf(i);
+ mAppStartInfoTracker.onIntentStarted(buildIntent(COMPONENT), startTime);
+ verifyInProgressRecordsSize(
+ Math.min(i + 1, AppStartInfoTracker.MAX_IN_PROGRESS_RECORDS));
+
+ mAppStartInfoTracker.onActivityLaunched(startTime, COMPONENT,
+ ApplicationStartInfo.START_TYPE_COLD, app);
+ verifyInProgressRecordsSize(
+ Math.min(i + 1, AppStartInfoTracker.MAX_IN_PROGRESS_RECORDS));
+
+ mAppStartInfoTracker.onActivityLaunchFinished(startTime, COMPONENT,
+ startTime + 100, ApplicationStartInfo.LAUNCH_MODE_STANDARD);
+ verifyInProgressRecordsSize(
+ Math.min(i + 1, AppStartInfoTracker.MAX_IN_PROGRESS_RECORDS));
+
+ // Make sure that the record added in this iteration is still present.
+ assertTrue(mAppStartInfoTracker.mInProgressRecords.containsKey(startTime));
+ }
+
+ // Confirm that after 2 x MAX_IN_PROGRESS_RECORDS starts only MAX_IN_PROGRESS_RECORDS are
+ // present.
+ verifyInProgressRecordsSize(AppStartInfoTracker.MAX_IN_PROGRESS_RECORDS);
+ }
+
private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) {
try {
Field field = clazz.getDeclaredField(fieldName);
@@ -484,16 +529,16 @@
private void verifyInProgressRecordsSize(int expectedSize) {
synchronized (mAppStartInfoTracker.mLock) {
- assertEquals(mAppStartInfoTracker.mInProgRecords.size(), expectedSize);
+ assertEquals(mAppStartInfoTracker.mInProgressRecords.size(), expectedSize);
}
}
- private void verifyInProgApplicationStartInfo(int index,
+ private void verifyInProgressApplicationStartInfo(int index,
Integer pid, Integer uid, Integer packageUid,
Integer definingUid, String processName,
Integer reason, Integer startupState, Integer startType, Integer launchMode) {
synchronized (mAppStartInfoTracker.mLock) {
- verifyApplicationStartInfo(mAppStartInfoTracker.mInProgRecords.valueAt(index),
+ verifyApplicationStartInfo(mAppStartInfoTracker.mInProgressRecords.valueAt(index),
pid, uid, packageUid, definingUid, processName, reason, startupState,
startType, launchMode);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 28c7fb2..488ce66 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -44,6 +44,7 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static com.android.server.am.ActivityManagerService.FOLLOW_UP_OOMADJUSTER_UPDATE_MSG;
import static com.android.server.am.ProcessList.BACKUP_APP_ADJ;
import static com.android.server.am.ProcessList.CACHED_APP_MAX_ADJ;
import static com.android.server.am.ProcessList.CACHED_APP_MIN_ADJ;
@@ -77,6 +78,7 @@
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doNothing;
@@ -113,11 +115,10 @@
import com.android.server.wm.WindowProcessController;
import org.junit.After;
-import org.junit.AfterClass;
import org.junit.Before;
-import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import java.io.File;
import java.lang.reflect.Field;
@@ -164,92 +165,86 @@
private static int sFirstCachedAdj = ProcessList.CACHED_APP_MIN_ADJ
+ ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
- private static Context sContext;
- private static PackageManagerInternal sPackageManagerInternal;
- private static ActivityManagerService sService;
+ private Context mContext;
+ private PackageManagerInternal mPackageManagerInternal;
+ private ActivityManagerService mService;
+ private OomAdjusterInjector mInjector = new OomAdjusterInjector();
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@SuppressWarnings("GuardedBy")
- @BeforeClass
- public static void setUpOnce() {
- sContext = getInstrumentation().getTargetContext();
+ @Before
+ public void setUp() {
+ mContext = getInstrumentation().getTargetContext();
System.setProperty("dexmaker.share_classloader", "true");
- sPackageManagerInternal = mock(PackageManagerInternal.class);
- doReturn(new ComponentName("", "")).when(sPackageManagerInternal)
+ mPackageManagerInternal = mock(PackageManagerInternal.class);
+ doReturn(new ComponentName("", "")).when(mPackageManagerInternal)
.getSystemUiServiceComponent();
// Remove stale instance of PackageManagerInternal if there is any
LocalServices.removeServiceForTest(PackageManagerInternal.class);
- LocalServices.addService(PackageManagerInternal.class, sPackageManagerInternal);
+ LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
- sService = mock(ActivityManagerService.class);
- sService.mActivityTaskManager = new ActivityTaskManagerService(sContext);
- sService.mActivityTaskManager.initialize(null, null, sContext.getMainLooper());
- sService.mPackageManagerInt = sPackageManagerInternal;
- sService.mAtmInternal = spy(sService.mActivityTaskManager.getAtmInternal());
+ mService = mock(ActivityManagerService.class);
+ mService.mActivityTaskManager = new ActivityTaskManagerService(mContext);
+ mService.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
+ mService.mPackageManagerInt = mPackageManagerInternal;
+ mService.mAtmInternal = spy(mService.mActivityTaskManager.getAtmInternal());
- sService.mConstants = new ActivityManagerConstants(sContext, sService,
- sContext.getMainThreadHandler());
- setFieldValue(ActivityManagerService.class, sService, "mContext",
- sContext);
+ mService.mConstants = new ActivityManagerConstants(mContext, mService,
+ mContext.getMainThreadHandler());
+ setFieldValue(ActivityManagerService.class, mService, "mContext",
+ mContext);
ProcessList pr = spy(new ProcessList());
- pr.mService = sService;
+ pr.mService = mService;
AppProfiler profiler = mock(AppProfiler.class);
- setFieldValue(ActivityManagerService.class, sService, "mProcessList",
+ setFieldValue(ActivityManagerService.class, mService, "mProcessList",
pr);
- setFieldValue(ActivityManagerService.class, sService, "mHandler",
+ setFieldValue(ActivityManagerService.class, mService, "mHandler",
mock(ActivityManagerService.MainHandler.class));
- setFieldValue(ActivityManagerService.class, sService, "mProcessStats",
- new ProcessStatsService(sService, new File(sContext.getFilesDir(), "procstats")));
- setFieldValue(ActivityManagerService.class, sService, "mBackupTargets",
+ setFieldValue(ActivityManagerService.class, mService, "mProcessStats",
+ new ProcessStatsService(mService, new File(mContext.getFilesDir(), "procstats")));
+ setFieldValue(ActivityManagerService.class, mService, "mBackupTargets",
mock(SparseArray.class));
- setFieldValue(ActivityManagerService.class, sService, "mUserController",
+ setFieldValue(ActivityManagerService.class, mService, "mUserController",
mock(UserController.class));
- setFieldValue(ActivityManagerService.class, sService, "mAppProfiler", profiler);
- setFieldValue(ActivityManagerService.class, sService, "mProcLock",
+ setFieldValue(ActivityManagerService.class, mService, "mAppProfiler", profiler);
+ setFieldValue(ActivityManagerService.class, mService, "mProcLock",
new ActivityManagerProcLock());
- setFieldValue(ActivityManagerService.class, sService, "mServices",
- spy(new ActiveServices(sService)));
- setFieldValue(ActivityManagerService.class, sService, "mInternal",
+ setFieldValue(ActivityManagerService.class, mService, "mServices",
+ spy(new ActiveServices(mService)));
+ setFieldValue(ActivityManagerService.class, mService, "mInternal",
mock(ActivityManagerService.LocalService.class));
- setFieldValue(ActivityManagerService.class, sService, "mBatteryStatsService",
+ setFieldValue(ActivityManagerService.class, mService, "mBatteryStatsService",
mock(BatteryStatsService.class));
- setFieldValue(ActivityManagerService.class, sService, "mInjector",
- new ActivityManagerService.Injector(sContext));
- doReturn(mock(AppOpsManager.class)).when(sService).getAppOpsManager();
- doCallRealMethod().when(sService).enqueueOomAdjTargetLocked(any(ProcessRecord.class));
- doCallRealMethod().when(sService).updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_ACTIVITY);
+ setFieldValue(ActivityManagerService.class, mService, "mInjector",
+ new ActivityManagerService.Injector(mContext));
+ doReturn(mock(AppOpsManager.class)).when(mService).getAppOpsManager();
+ doCallRealMethod().when(mService).enqueueOomAdjTargetLocked(any(ProcessRecord.class));
+ doCallRealMethod().when(mService).updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_ACTIVITY);
setFieldValue(AppProfiler.class, profiler, "mProfilerLock", new Object());
doReturn(new ActivityManagerService.ProcessChangeItem()).when(pr)
.enqueueProcessChangeItemLocked(anyInt(), anyInt());
- sService.mOomAdjuster = sService.mConstants.ENABLE_NEW_OOMADJ
- ? new OomAdjusterModernImpl(sService, sService.mProcessList,
- new ActiveUids(sService, false))
- : new OomAdjuster(sService, sService.mProcessList, new ActiveUids(sService, false));
- sService.mOomAdjuster.mAdjSeq = 10000;
- sService.mWakefulness = new AtomicInteger(PowerManagerInternal.WAKEFULNESS_AWAKE);
- if (sService.mConstants.USE_TIERED_CACHED_ADJ) {
+ mService.mOomAdjuster = mService.mConstants.ENABLE_NEW_OOMADJ
+ ? new OomAdjusterModernImpl(mService, mService.mProcessList,
+ new ActiveUids(mService, false), mInjector)
+ : new OomAdjuster(mService, mService.mProcessList, new ActiveUids(mService, false),
+ mInjector);
+ mService.mOomAdjuster.mAdjSeq = 10000;
+ mService.mWakefulness = new AtomicInteger(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ if (mService.mConstants.USE_TIERED_CACHED_ADJ) {
sFirstCachedAdj = ProcessList.CACHED_APP_MIN_ADJ + 10;
}
- }
-
- @Before
- public void setUp() {
mSetFlagsRule.enableFlags(Flags.FLAG_NEW_FGS_RESTRICTION_LOGIC);
}
- @AfterClass
- public static void tearDownOnce() {
- LocalServices.removeServiceForTest(PackageManagerInternal.class);
- }
-
@SuppressWarnings("GuardedBy")
@After
public void tearDown() {
- sService.mOomAdjuster.resetInternal();
- sService.mOomAdjuster.mActiveUids.clear();
+ mService.mOomAdjuster.resetInternal();
+ mService.mOomAdjuster.mActiveUids.clear();
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
}
private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) {
@@ -278,7 +273,7 @@
*/
@SuppressWarnings("GuardedBy")
private void setProcessesToLru(ProcessRecord... apps) {
- ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
+ ArrayList<ProcessRecord> lru = mService.mProcessList.getLruProcessesLOSP();
lru.clear();
Collections.addAll(lru, apps);
}
@@ -292,20 +287,20 @@
@SuppressWarnings("GuardedBy")
private void updateOomAdj(ProcessRecord... apps) {
if (apps.length == 0) {
- updateProcessRecordNodes(sService.mProcessList.getLruProcessesLOSP());
- sService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE);
+ updateProcessRecordNodes(mService.mProcessList.getLruProcessesLOSP());
+ mService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE);
} else {
updateProcessRecordNodes(Arrays.asList(apps));
if (apps.length == 1) {
final ProcessRecord app = apps[0];
- if (!sService.mProcessList.getLruProcessesLOSP().contains(app)) {
- sService.mProcessList.getLruProcessesLOSP().add(app);
+ if (!mService.mProcessList.getLruProcessesLOSP().contains(app)) {
+ mService.mProcessList.getLruProcessesLOSP().add(app);
}
- sService.mOomAdjuster.updateOomAdjLocked(apps[0], OOM_ADJ_REASON_NONE);
+ mService.mOomAdjuster.updateOomAdjLocked(apps[0], OOM_ADJ_REASON_NONE);
} else {
setProcessesToLru(apps);
- sService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE);
- sService.mProcessList.getLruProcessesLOSP().clear();
+ mService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE);
+ mService.mProcessList.getLruProcessesLOSP().clear();
}
}
}
@@ -318,10 +313,10 @@
private void updateOomAdjPending(ProcessRecord... apps) {
setProcessesToLru(apps);
for (ProcessRecord app : apps) {
- sService.mOomAdjuster.enqueueOomAdjTargetLocked(app);
+ mService.mOomAdjuster.enqueueOomAdjTargetLocked(app);
}
- sService.mOomAdjuster.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_NONE);
- sService.mProcessList.getLruProcessesLOSP().clear();
+ mService.mOomAdjuster.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_NONE);
+ mService.mProcessList.getLruProcessesLOSP().clear();
}
/**
@@ -343,9 +338,9 @@
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
app.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
app.mState.setHasTopUi(true);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP);
updateOomAdj(app);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERSISTENT_PROC_ADJ,
SCHED_GROUP_RESTRICTED);
@@ -359,7 +354,7 @@
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
app.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
app.mState.setHasTopUi(true);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
assertProcStates(app, PROCESS_STATE_PERSISTENT_UI, PERSISTENT_PROC_ADJ,
@@ -372,10 +367,10 @@
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
app.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
- doReturn(app).when(sService).getTopApp();
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ doReturn(app).when(mService).getTopApp();
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
- doReturn(null).when(sService).getTopApp();
+ doReturn(null).when(mService).getTopApp();
assertProcStates(app, PROCESS_STATE_PERSISTENT_UI, PERSISTENT_PROC_ADJ,
SCHED_GROUP_TOP_APP);
@@ -386,11 +381,11 @@
public void testUpdateOomAdj_DoOne_TopApp_Awake() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
- doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
- doReturn(app).when(sService).getTopApp();
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
+ doReturn(app).when(mService).getTopApp();
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
- doReturn(null).when(sService).getTopApp();
+ doReturn(null).when(mService).getTopApp();
assertProcStates(app, PROCESS_STATE_TOP, FOREGROUND_APP_ADJ, SCHED_GROUP_TOP_APP);
}
@@ -400,11 +395,11 @@
public void testUpdateOomAdj_DoOne_RunningAnimations() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
- doReturn(PROCESS_STATE_TOP_SLEEPING).when(sService.mAtmInternal).getTopProcessState();
+ doReturn(PROCESS_STATE_TOP_SLEEPING).when(mService.mAtmInternal).getTopProcessState();
app.mState.setRunningRemoteAnimation(true);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
- doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
+ doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
assertProcStates(app, PROCESS_STATE_TOP_SLEEPING, VISIBLE_APP_ADJ, SCHED_GROUP_TOP_APP);
}
@@ -415,7 +410,7 @@
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
doReturn(mock(ActiveInstrumentation.class)).when(app).getActiveInstrumentation();
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
doCallRealMethod().when(app).getActiveInstrumentation();
@@ -429,11 +424,11 @@
public void testUpdateOomAdj_DoOne_ReceivingBroadcast() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
- doReturn(true).when(sService).isReceivingBroadcastLocked(any(ProcessRecord.class),
+ doReturn(true).when(mService).isReceivingBroadcastLocked(any(ProcessRecord.class),
any(int[].class));
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
- doReturn(false).when(sService).isReceivingBroadcastLocked(any(ProcessRecord.class),
+ doReturn(false).when(mService).isReceivingBroadcastLocked(any(ProcessRecord.class),
any(int[].class));
assertProcStates(app, PROCESS_STATE_RECEIVER, FOREGROUND_APP_ADJ, SCHED_GROUP_BACKGROUND);
@@ -445,7 +440,7 @@
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
app.mServices.startExecutingService(mock(ServiceRecord.class));
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
assertProcStates(app, PROCESS_STATE_SERVICE, FOREGROUND_APP_ADJ, SCHED_GROUP_BACKGROUND);
@@ -456,13 +451,13 @@
public void testUpdateOomAdj_DoOne_TopApp_Sleeping() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
- doReturn(PROCESS_STATE_TOP_SLEEPING).when(sService.mAtmInternal).getTopProcessState();
- doReturn(app).when(sService).getTopApp();
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP);
+ doReturn(PROCESS_STATE_TOP_SLEEPING).when(mService.mAtmInternal).getTopProcessState();
+ doReturn(app).when(mService).getTopApp();
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP);
updateOomAdj(app);
- doReturn(null).when(sService).getTopApp();
- doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ doReturn(null).when(mService).getTopApp();
+ doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
assertProcStates(app, PROCESS_STATE_TOP_SLEEPING, FOREGROUND_APP_ADJ,
SCHED_GROUP_BACKGROUND);
@@ -475,8 +470,8 @@
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
app.mState.setCurRawAdj(CACHED_APP_MIN_ADJ);
app.mState.setCurAdj(CACHED_APP_MIN_ADJ);
- doReturn(null).when(sService).getTopApp();
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ doReturn(null).when(mService).getTopApp();
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
final int expectedAdj = sFirstCachedAdj;
@@ -505,7 +500,7 @@
return 0;
})).when(wpc).computeOomAdjFromActivities(
any(WindowProcessController.ComputeOomAdjCallback.class));
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
assertProcStates(app, PROCESS_STATE_TOP, VISIBLE_APP_ADJ, SCHED_GROUP_TOP_APP);
@@ -522,7 +517,7 @@
WindowProcessController wpc = app.getWindowProcessController();
doReturn(true).when(wpc).hasRecentTasks();
app.mState.setLastTopTime(SystemClock.uptimeMillis());
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
doCallRealMethod().when(wpc).hasRecentTasks();
@@ -536,7 +531,7 @@
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
app.mServices.setHasForegroundServices(true, ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION,
/* hasNoneType=*/false);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -550,7 +545,7 @@
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
app.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -561,10 +556,10 @@
@SuppressWarnings("GuardedBy")
@Test
public void testUpdateOomAdj_DoOne_FgService_ShortFgs() {
- sService.mConstants.TOP_TO_FGS_GRACE_DURATION = 100_000;
- sService.mConstants.mShortFgsProcStateExtraWaitDuration = 200_000;
+ mService.mConstants.TOP_TO_FGS_GRACE_DURATION = 100_000;
+ mService.mConstants.mShortFgsProcStateExtraWaitDuration = 200_000;
- ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(sService);
+ ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(mService);
s.appInfo = new ApplicationInfo();
s.startRequested = true;
s.isForeground = true;
@@ -579,7 +574,7 @@
app.mServices.setHasForegroundServices(true,
FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, /* hasNoneType=*/false);
app.mState.setLastTopTime(SystemClock.uptimeMillis());
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
@@ -596,8 +591,8 @@
FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, /* hasNoneType=*/false);
app.mServices.startService(s);
app.mState.setLastTopTime(SystemClock.uptimeMillis()
- - sService.mConstants.TOP_TO_FGS_GRACE_DURATION);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ - mService.mConstants.TOP_TO_FGS_GRACE_DURATION);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
@@ -607,14 +602,14 @@
}
// SHORT_SERVICE, timed out already.
- s = ServiceRecord.newEmptyInstanceForTest(sService);
+ s = ServiceRecord.newEmptyInstanceForTest(mService);
s.appInfo = new ApplicationInfo();
s.startRequested = true;
s.isForeground = true;
s.foregroundServiceType = FOREGROUND_SERVICE_TYPE_SHORT_SERVICE;
s.setShortFgsInfo(SystemClock.uptimeMillis()
- - sService.mConstants.mShortFgsTimeoutDuration
- - sService.mConstants.mShortFgsProcStateExtraWaitDuration);
+ - mService.mConstants.mShortFgsTimeoutDuration
+ - mService.mConstants.mShortFgsProcStateExtraWaitDuration);
{
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
@@ -622,8 +617,8 @@
FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, /* hasNoneType=*/false);
app.mServices.startService(s);
app.mState.setLastTopTime(SystemClock.uptimeMillis()
- - sService.mConstants.TOP_TO_FGS_GRACE_DURATION);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ - mService.mConstants.TOP_TO_FGS_GRACE_DURATION);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
@@ -639,7 +634,7 @@
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
app.mState.setHasOverlayUi(true);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
assertProcStates(app, PROCESS_STATE_IMPORTANT_FOREGROUND, PERCEPTIBLE_APP_ADJ,
@@ -653,12 +648,26 @@
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
app.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
app.mState.setLastTopTime(SystemClock.uptimeMillis());
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE,
- PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ, SCHED_GROUP_DEFAULT);
+ PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ, SCHED_GROUP_DEFAULT, "fg-service-act");
assertBfsl(app);
+
+ if (!Flags.followUpOomadjUpdates()) return;
+
+ final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class);
+ verify(mService.mHandler).sendEmptyMessageAtTime(
+ eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture());
+ mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
+ mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
+
+ assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
+ SCHED_GROUP_DEFAULT, "fg-service");
+ // Follow up should not have been called again.
+ verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG),
+ followUpTimeCaptor.capture());
}
@SuppressWarnings("GuardedBy")
@@ -678,12 +687,24 @@
s.lastTopAlmostPerceptibleBindRequestUptimeMs = nowUptime;
s.getConnections().clear();
app.mServices.updateHasTopStartedAlmostPerceptibleServices();
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
assertEquals(PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ + 2, app.mState.getSetAdj());
- sService.mOomAdjuster.resetInternal();
+ if (!Flags.followUpOomadjUpdates()) return;
+
+ final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class);
+ verify(mService.mHandler).sendEmptyMessageAtTime(
+ eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture());
+ mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
+ mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
+
+ assertEquals(sFirstCachedAdj, app.mState.getSetAdj());
+ // Follow up should not have been called again.
+ verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG),
+ followUpTimeCaptor.capture());
+
}
// Out of grace period but valid binding allows the adjustment.
@@ -698,14 +719,14 @@
ServiceRecord s = bindService(app, system,
null, null, Context.BIND_ALMOST_PERCEPTIBLE + 2, mock(IBinder.class));
s.lastTopAlmostPerceptibleBindRequestUptimeMs =
- nowUptime - 2 * sService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs;
+ nowUptime - 2 * mService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs;
app.mServices.updateHasTopStartedAlmostPerceptibleServices();
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
assertEquals(PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ + 2, app.mState.getSetAdj());
- sService.mOomAdjuster.resetInternal();
+ mService.mOomAdjuster.resetInternal();
}
// Out of grace period and no valid binding so no adjustment.
@@ -720,15 +741,15 @@
ServiceRecord s = bindService(app, system,
null, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class));
s.lastTopAlmostPerceptibleBindRequestUptimeMs =
- nowUptime - 2 * sService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs;
+ nowUptime - 2 * mService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs;
s.getConnections().clear();
app.mServices.updateHasTopStartedAlmostPerceptibleServices();
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
assertNotEquals(PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ + 2, app.mState.getSetAdj());
- sService.mOomAdjuster.resetInternal();
+ mService.mOomAdjuster.resetInternal();
}
}
@@ -744,7 +765,7 @@
// Simulate the system starting and binding to a service in the app.
ServiceRecord s = bindService(app, system,
null, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class));
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(system, app);
assertProcStates(app, PROCESS_STATE_IMPORTANT_FOREGROUND,
@@ -757,7 +778,7 @@
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
app.mState.setForcingToImportant(new Object());
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, PERCEPTIBLE_APP_ADJ,
@@ -771,7 +792,7 @@
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
WindowProcessController wpc = app.getWindowProcessController();
doReturn(true).when(wpc).isHeavyWeightProcess();
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
doReturn(false).when(wpc).isHeavyWeightProcess();
@@ -786,7 +807,7 @@
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
WindowProcessController wpc = app.getWindowProcessController();
doReturn(true).when(wpc).isHomeProcess();
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
assertProcStates(app, PROCESS_STATE_HOME, HOME_APP_ADJ, SCHED_GROUP_BACKGROUND);
@@ -800,11 +821,25 @@
WindowProcessController wpc = app.getWindowProcessController();
doReturn(true).when(wpc).isPreviousProcess();
doReturn(true).when(wpc).hasActivities();
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ,
- SCHED_GROUP_BACKGROUND);
+ SCHED_GROUP_BACKGROUND, "previous");
+
+ if (!Flags.followUpOomadjUpdates()) return;
+
+ final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class);
+ verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG),
+ followUpTimeCaptor.capture());
+ mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
+ mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
+
+ assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, CACHED_APP_MIN_ADJ,
+ SCHED_GROUP_BACKGROUND, "previous-expired");
+ // Follow up should not have been called again.
+ verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG),
+ followUpTimeCaptor.capture());
}
@SuppressWarnings("GuardedBy")
@@ -814,10 +849,10 @@
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
BackupRecord backupTarget = new BackupRecord(null, 0, 0, 0);
backupTarget.app = app;
- doReturn(backupTarget).when(sService.mBackupTargets).get(anyInt());
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ doReturn(backupTarget).when(mService.mBackupTargets).get(anyInt());
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
- doReturn(null).when(sService.mBackupTargets).get(anyInt());
+ doReturn(null).when(mService.mBackupTargets).get(anyInt());
assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, BACKUP_APP_ADJ,
SCHED_GROUP_BACKGROUND);
@@ -829,7 +864,7 @@
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
app.mServices.setHasClientActivities(true);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
assertEquals(PROCESS_STATE_CACHED_ACTIVITY_CLIENT, app.mState.getSetProcState());
@@ -841,7 +876,7 @@
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
app.mServices.setTreatLikeActivity(true);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
assertEquals(PROCESS_STATE_CACHED_ACTIVITY, app.mState.getSetProcState());
@@ -858,7 +893,7 @@
s.startRequested = true;
s.lastActivity = SystemClock.uptimeMillis();
app.mServices.startService(s);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_B_ADJ, SCHED_GROUP_BACKGROUND);
@@ -870,7 +905,7 @@
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
app.mState.setMaxAdj(PERCEPTIBLE_LOW_APP_ADJ);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, PERCEPTIBLE_LOW_APP_ADJ,
@@ -884,8 +919,8 @@
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
app.mState.setCurRawAdj(SERVICE_ADJ);
app.mState.setCurAdj(SERVICE_ADJ);
- doReturn(null).when(sService).getTopApp();
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ doReturn(null).when(mService).getTopApp();
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
assertTrue(ProcessList.CACHED_APP_MIN_ADJ <= app.mState.getSetAdj());
@@ -902,7 +937,7 @@
s.startRequested = true;
s.lastActivity = SystemClock.uptimeMillis();
app.mServices.startService(s);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND);
@@ -918,11 +953,11 @@
ServiceRecord s = bindService(app, client, null, null, Context.BIND_WAIVE_PRIORITY,
mock(IBinder.class));
s.startRequested = true;
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
- doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
- doReturn(client).when(sService).getTopApp();
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
+ doReturn(client).when(mService).getTopApp();
updateOomAdj(client, app);
- doReturn(null).when(sService).getTopApp();
+ doReturn(null).when(mService).getTopApp();
assertProcStates(app, PROCESS_STATE_SERVICE, sFirstCachedAdj, SCHED_GROUP_BACKGROUND);
}
@@ -937,7 +972,7 @@
client.mServices.setTreatLikeActivity(true);
bindService(app, client, null, null, Context.BIND_WAIVE_PRIORITY
| Context.BIND_TREAT_LIKE_ACTIVITY, mock(IBinder.class));
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
assertEquals(PROCESS_STATE_CACHED_ACTIVITY, app.mState.getSetProcState());
@@ -956,9 +991,9 @@
ConnectionRecord cr = s.getConnections().get(binder).get(0);
setFieldValue(ConnectionRecord.class, cr, "activity",
mock(ActivityServiceConnectionsHolder.class));
- doReturn(client).when(sService).getTopApp();
+ doReturn(client).when(mService).getTopApp();
doReturn(true).when(cr.activity).isActivityVisible();
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj());
@@ -971,7 +1006,7 @@
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
bindService(app, app, null, null, 0, mock(IBinder.class));
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND);
@@ -986,7 +1021,7 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
client.mServices.setTreatLikeActivity(true);
bindService(app, client, null, null, 0, mock(IBinder.class));
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
assertEquals(PROCESS_STATE_CACHED_EMPTY, app.mState.getSetProcState());
@@ -1005,11 +1040,11 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, null, Context.BIND_ALLOW_OOM_MANAGEMENT,
mock(IBinder.class));
- doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
- doReturn(client).when(sService).getTopApp();
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
+ doReturn(client).when(mService).getTopApp();
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
- doReturn(null).when(sService).getTopApp();
+ doReturn(null).when(mService).getTopApp();
assertEquals(PREVIOUS_APP_ADJ, app.mState.getSetAdj());
}
@@ -1024,7 +1059,7 @@
bindService(app, client, null, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class));
client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
client.mState.setHasTopUi(true);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, VISIBLE_APP_ADJ,
@@ -1041,7 +1076,7 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, null, Context.BIND_IMPORTANT, mock(IBinder.class));
client.mServices.startExecutingService(mock(ServiceRecord.class));
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj());
@@ -1056,11 +1091,11 @@
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, null, 0, mock(IBinder.class));
- doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
- doReturn(client).when(sService).getTopApp();
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
+ doReturn(client).when(mService).getTopApp();
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
- doReturn(null).when(sService).getTopApp();
+ doReturn(null).when(mService).getTopApp();
assertProcStates(app, PROCESS_STATE_BOUND_TOP, VISIBLE_APP_ADJ, SCHED_GROUP_DEFAULT);
}
@@ -1074,7 +1109,7 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class));
client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
assertEquals(PROCESS_STATE_BOUND_FOREGROUND_SERVICE, app.mState.getSetProcState());
@@ -1092,7 +1127,7 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, null, Context.BIND_NOT_FOREGROUND, mock(IBinder.class));
client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
assertEquals(PROCESS_STATE_TRANSIENT_BACKGROUND, app.mState.getSetProcState());
@@ -1108,7 +1143,7 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, null, 0, mock(IBinder.class));
client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
assertEquals(PROCESS_STATE_FOREGROUND_SERVICE, client.mState.getSetProcState());
@@ -1128,7 +1163,7 @@
bindService(app, client, null, null, 0, mock(IBinder.class));
// In order to trick OomAdjuster to think it has a short-service, we need this logic.
- ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(sService);
+ ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(mService);
s.appInfo = new ApplicationInfo();
s.startRequested = true;
s.isForeground = true;
@@ -1139,7 +1174,7 @@
client.mServices.setHasForegroundServices(true, FOREGROUND_SERVICE_TYPE_SHORT_SERVICE,
/* hasNoneType=*/false);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
// Client only has a SHORT_FGS, so it doesn't have BFSL, and that's propagated.
@@ -1159,7 +1194,7 @@
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
// In order to trick OomAdjuster to think it has a short-service, we need this logic.
- ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(sService);
+ ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(mService);
s.appInfo = new ApplicationInfo();
s.startRequested = true;
s.isForeground = true;
@@ -1170,7 +1205,7 @@
app2.mServices.setHasForegroundServices(true, FOREGROUND_SERVICE_TYPE_SHORT_SERVICE,
/* hasNoneType=*/false);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app2);
// Client only has a SHORT_FGS, so it doesn't have BFSL, and that's propagated.
@@ -1211,11 +1246,11 @@
bindService(app, client, null, null, Context.BIND_ABOVE_CLIENT, mock(IBinder.class));
BackupRecord backupTarget = new BackupRecord(null, 0, 0, 0);
backupTarget.app = client;
- doReturn(backupTarget).when(sService.mBackupTargets).get(anyInt());
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ doReturn(backupTarget).when(mService.mBackupTargets).get(anyInt());
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
- doReturn(null).when(sService.mBackupTargets).get(anyInt());
+ doReturn(null).when(mService.mBackupTargets).get(anyInt());
assertEquals(BACKUP_APP_ADJ, app.mState.getSetAdj());
assertNoBfsl(app);
@@ -1236,7 +1271,7 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, null, Context.BIND_NOT_PERCEPTIBLE, mock(IBinder.class));
client.mState.setRunningRemoteAnimation(true);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
assertEquals(PERCEPTIBLE_LOW_APP_ADJ, app.mState.getSetAdj());
@@ -1251,7 +1286,7 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, null, Context.BIND_NOT_VISIBLE, mock(IBinder.class));
client.mState.setRunningRemoteAnimation(true);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
assertEquals(PERCEPTIBLE_APP_ADJ, app.mState.getSetAdj());
@@ -1266,7 +1301,7 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, null, 0, mock(IBinder.class));
client.mState.setHasOverlayUi(true);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
assertEquals(PERCEPTIBLE_APP_ADJ, app.mState.getSetAdj());
@@ -1284,12 +1319,12 @@
Context.BIND_ALMOST_PERCEPTIBLE | Context.BIND_NOT_FOREGROUND,
mock(IBinder.class));
client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
assertEquals(PERCEPTIBLE_MEDIUM_APP_ADJ + 2, app.mState.getSetAdj());
- sService.mOomAdjuster.resetInternal();
+ mService.mOomAdjuster.resetInternal();
}
{
@@ -1303,13 +1338,13 @@
Context.BIND_ALMOST_PERCEPTIBLE | Context.BIND_NOT_FOREGROUND,
mock(IBinder.class));
client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
doReturn(false).when(wpc).isHeavyWeightProcess();
assertEquals(PERCEPTIBLE_MEDIUM_APP_ADJ + 2, app.mState.getSetAdj());
- sService.mOomAdjuster.resetInternal();
+ mService.mOomAdjuster.resetInternal();
}
{
@@ -1321,12 +1356,12 @@
Context.BIND_ALMOST_PERCEPTIBLE,
mock(IBinder.class));
client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
assertEquals(PERCEPTIBLE_APP_ADJ + 1, app.mState.getSetAdj());
- sService.mOomAdjuster.resetInternal();
+ mService.mOomAdjuster.resetInternal();
}
{
@@ -1340,13 +1375,13 @@
Context.BIND_ALMOST_PERCEPTIBLE,
mock(IBinder.class));
client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
doReturn(false).when(wpc).isHeavyWeightProcess();
assertEquals(PERCEPTIBLE_APP_ADJ + 1, app.mState.getSetAdj());
- sService.mOomAdjuster.resetInternal();
+ mService.mOomAdjuster.resetInternal();
}
}
@@ -1359,7 +1394,7 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindService(app, client, null, null, 0, mock(IBinder.class));
client.mState.setRunningRemoteAnimation(true);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
assertEquals(VISIBLE_APP_ADJ, app.mState.getSetAdj());
@@ -1375,7 +1410,7 @@
bindService(app, client, null, null, Context.BIND_IMPORTANT_BACKGROUND,
mock(IBinder.class));
client.mState.setHasOverlayUi(true);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
assertEquals(PROCESS_STATE_IMPORTANT_BACKGROUND, app.mState.getSetProcState());
@@ -1402,7 +1437,7 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindProvider(app, client, null, null, false);
client.mServices.setTreatLikeActivity(true);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app, client);
assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND);
@@ -1416,11 +1451,11 @@
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindProvider(app, client, null, null, false);
- doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
- doReturn(client).when(sService).getTopApp();
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
+ doReturn(client).when(mService).getTopApp();
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
- doReturn(null).when(sService).getTopApp();
+ doReturn(null).when(mService).getTopApp();
assertProcStates(app, PROCESS_STATE_BOUND_TOP, FOREGROUND_APP_ADJ, SCHED_GROUP_DEFAULT);
}
@@ -1434,7 +1469,7 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
bindProvider(app, client, null, null, false);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1452,7 +1487,7 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
// In order to trick OomAdjuster to think it has a short-service, we need this logic.
- ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(sService);
+ ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(mService);
s.appInfo = new ApplicationInfo();
s.startRequested = true;
s.isForeground = true;
@@ -1464,7 +1499,7 @@
client.mServices.setHasForegroundServices(true, FOREGROUND_SERVICE_TYPE_SHORT_SERVICE,
/* hasNoneType=*/false);
bindProvider(app, client, null, null, false);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
// Client only has a SHORT_FGS, so it doesn't have BFSL, and that's propagated.
@@ -1486,7 +1521,7 @@
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
bindProvider(app, client, null, null, true);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
assertProcStates(app, PROCESS_STATE_IMPORTANT_FOREGROUND, FOREGROUND_APP_ADJ,
@@ -1499,11 +1534,25 @@
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
app.mProviders.setLastProviderTime(SystemClock.uptimeMillis());
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ,
- SCHED_GROUP_BACKGROUND);
+ SCHED_GROUP_BACKGROUND, "recent-provider");
+
+ if (!Flags.followUpOomadjUpdates()) return;
+
+ final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class);
+ verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG),
+ followUpTimeCaptor.capture());
+ mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
+ mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
+
+ assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND,
+ "cch-empty");
+ // Follow up should not have been called again.
+ verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG),
+ followUpTimeCaptor.capture());
}
@SuppressWarnings("GuardedBy")
@@ -1517,11 +1566,11 @@
ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
bindService(client, client2, null, null, 0, mock(IBinder.class));
- doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
- doReturn(client2).when(sService).getTopApp();
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
+ doReturn(client2).when(mService).getTopApp();
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, client2, app);
- doReturn(null).when(sService).getTopApp();
+ doReturn(null).when(mService).getTopApp();
assertProcStates(app, PROCESS_STATE_BOUND_TOP, VISIBLE_APP_ADJ,
SCHED_GROUP_DEFAULT);
@@ -1539,7 +1588,7 @@
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
bindService(app, client2, null, null, 0, mock(IBinder.class));
client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, client2, app);
assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1559,7 +1608,7 @@
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
bindService(client, client2, null, null, 0, mock(IBinder.class));
client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, client2, app);
assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1584,7 +1633,7 @@
// Note: We add processes to LRU but still call updateOomAdjLocked() with a specific
// processes.
setProcessesToLru(app, client, client2);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1598,7 +1647,7 @@
assertBfsl(client2);
client2.mServices.setHasForegroundServices(false, 0, /* hasNoneType=*/false);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client2);
assertEquals(PROCESS_STATE_CACHED_EMPTY, client2.mState.getSetProcState());
@@ -1622,7 +1671,7 @@
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
bindService(client2, client, null, null, 0, mock(IBinder.class));
client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app, client, client2);
assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1649,7 +1698,7 @@
bindService(client, client2, null, null, 0, mock(IBinder.class));
bindService(client2, client, null, null, 0, mock(IBinder.class));
client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app, client, client2);
assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1683,7 +1732,7 @@
bindService(client3, client4, null, null, 0, mock(IBinder.class));
bindService(client4, client3, null, null, 0, mock(IBinder.class));
client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app, client, client2, client3, client4);
assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1720,7 +1769,7 @@
MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
client3.mState.setForcingToImportant(new Object());
bindService(app, client3, null, null, 0, mock(IBinder.class));
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app, client, client2, client3);
assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1746,7 +1795,7 @@
MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
client3.mState.setForcingToImportant(new Object());
bindService(app, client3, null, null, 0, mock(IBinder.class));
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app, client, client2, client3);
assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, PERCEPTIBLE_APP_ADJ,
@@ -1773,7 +1822,7 @@
MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
client4.mState.setForcingToImportant(new Object());
bindService(app, client4, null, null, 0, mock(IBinder.class));
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app, client, client2, client3, client4);
assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, PERCEPTIBLE_APP_ADJ,
@@ -1802,7 +1851,7 @@
MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
client4.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
bindService(app, client4, null, null, 0, mock(IBinder.class));
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app, client, client2, client3, client4);
assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1828,7 +1877,7 @@
MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
client3.mState.setForcingToImportant(new Object());
bindService(app, client3, null, null, 0, mock(IBinder.class));
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, client2, client3, app);
assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1848,7 +1897,7 @@
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
bindProvider(client, client2, null, null, false);
client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, client2, app);
assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1869,7 +1918,7 @@
bindProvider(client, client2, null, null, false);
client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
bindService(client2, app, null, null, 0, mock(IBinder.class));
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app, client, client2);
assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1889,7 +1938,7 @@
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
bindProvider(client, client2, null, null, false);
client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, client2, app);
assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1910,7 +1959,7 @@
bindProvider(client, client2, null, null, false);
client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
bindProvider(client2, app, null, null, false);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app, client, client2);
assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1936,7 +1985,7 @@
client1.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client1, client2, app1, app2);
assertProcStates(app1, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, VISIBLE_APP_ADJ,
@@ -1957,7 +2006,7 @@
assertProcStates(app2, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
SCHED_GROUP_DEFAULT);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP);
updateOomAdj(client1, client2, app1, app2);
assertProcStates(app1, PROCESS_STATE_IMPORTANT_FOREGROUND, VISIBLE_APP_ADJ,
SCHED_GROUP_TOP_APP);
@@ -2028,9 +2077,9 @@
SCHED_GROUP_DEFAULT);
client2.mState.setHasOverlayUi(false);
- doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
- doReturn(client2).when(sService).getTopApp();
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
+ doReturn(client2).when(mService).getTopApp();
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client2, app2);
assertProcStates(app2, PROCESS_STATE_BOUND_TOP, VISIBLE_APP_ADJ,
@@ -2047,7 +2096,7 @@
client1.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
app1.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
bindService(app1, client1, null, null, Context.BIND_NOT_PERCEPTIBLE, mock(IBinder.class));
@@ -2068,7 +2117,7 @@
client1.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
app1.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
bindService(app1, client1, null, null, Context.BIND_ALMOST_PERCEPTIBLE,
mock(IBinder.class));
@@ -2088,7 +2137,7 @@
app.setPendingFinishAttach(true);
app.mState.setHasForegroundActivities(false);
- sService.mOomAdjuster.setAttachingProcessStatesLSP(app);
+ mService.mOomAdjuster.setAttachingProcessStatesLSP(app);
updateOomAdj(app);
assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, FOREGROUND_APP_ADJ,
@@ -2102,9 +2151,9 @@
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
app.setPendingFinishAttach(true);
app.mState.setHasForegroundActivities(true);
- doReturn(app).when(sService).getTopApp();
+ doReturn(app).when(mService).getTopApp();
- sService.mOomAdjuster.setAttachingProcessStatesLSP(app);
+ mService.mOomAdjuster.setAttachingProcessStatesLSP(app);
updateOomAdj(app);
assertProcStates(app, PROCESS_STATE_TOP, FOREGROUND_APP_ADJ,
@@ -2124,10 +2173,10 @@
MOCKAPP4_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
final ProcessRecord app3 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID,
MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
- final UidRecord app1UidRecord = new UidRecord(MOCKAPP_UID, sService);
- final UidRecord app2UidRecord = new UidRecord(MOCKAPP2_UID, sService);
- final UidRecord app3UidRecord = new UidRecord(MOCKAPP5_UID, sService);
- final UidRecord clientUidRecord = new UidRecord(MOCKAPP3_UID, sService);
+ final UidRecord app1UidRecord = new UidRecord(MOCKAPP_UID, mService);
+ final UidRecord app2UidRecord = new UidRecord(MOCKAPP2_UID, mService);
+ final UidRecord app3UidRecord = new UidRecord(MOCKAPP5_UID, mService);
+ final UidRecord clientUidRecord = new UidRecord(MOCKAPP3_UID, mService);
app1.setUidRecord(app1UidRecord);
app2.setUidRecord(app2UidRecord);
app3.setUidRecord(app3UidRecord);
@@ -2137,7 +2186,7 @@
client1.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
client2.mState.setForcingToImportant(new Object());
setProcessesToLru(app1, app2, app3, client1, client2);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
final ComponentName cn1 = ComponentName.unflattenFromString(
MOCKAPP_PACKAGENAME + "/.TestService");
@@ -2164,10 +2213,10 @@
c2s.startRequested = true;
try {
- sService.mOomAdjuster.mActiveUids.put(MOCKAPP_UID, app1UidRecord);
- sService.mOomAdjuster.mActiveUids.put(MOCKAPP2_UID, app2UidRecord);
- sService.mOomAdjuster.mActiveUids.put(MOCKAPP5_UID, app3UidRecord);
- sService.mOomAdjuster.mActiveUids.put(MOCKAPP3_UID, clientUidRecord);
+ mService.mOomAdjuster.mActiveUids.put(MOCKAPP_UID, app1UidRecord);
+ mService.mOomAdjuster.mActiveUids.put(MOCKAPP2_UID, app2UidRecord);
+ mService.mOomAdjuster.mActiveUids.put(MOCKAPP5_UID, app3UidRecord);
+ mService.mOomAdjuster.mActiveUids.put(MOCKAPP3_UID, clientUidRecord);
setServiceMap(s1, MOCKAPP_UID, cn1);
setServiceMap(s2, MOCKAPP2_UID, cn2);
@@ -2195,10 +2244,10 @@
app2UidRecord.setIdle(true);
app3UidRecord.setIdle(true);
clientUidRecord.setIdle(true);
- doReturn(ActivityManager.APP_START_MODE_DELAYED).when(sService)
+ doReturn(ActivityManager.APP_START_MODE_DELAYED).when(mService)
.getAppStartModeLOSP(anyInt(), any(String.class), anyInt(),
anyInt(), anyBoolean(), anyBoolean(), anyBoolean());
- doNothing().when(sService.mServices)
+ doNothing().when(mService.mServices)
.scheduleServiceTimeoutLocked(any(ProcessRecord.class));
updateOomAdj(client1, client2, app1, app2, app3);
@@ -2206,11 +2255,11 @@
assertEquals(PROCESS_STATE_SERVICE, app1.mState.getSetProcState());
assertEquals(PROCESS_STATE_SERVICE, client2.mState.getSetProcState());
} finally {
- doCallRealMethod().when(sService)
+ doCallRealMethod().when(mService)
.getAppStartModeLOSP(anyInt(), any(String.class), anyInt(),
anyInt(), anyBoolean(), anyBoolean(), anyBoolean());
- sService.mServices.mServiceMap.clear();
- sService.mOomAdjuster.mActiveUids.clear();
+ mService.mServices.mServiceMap.clear();
+ mService.mOomAdjuster.mActiveUids.clear();
}
}
@@ -2224,7 +2273,7 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
app2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app, app2);
assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, PERCEPTIBLE_APP_ADJ,
@@ -2244,7 +2293,7 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
app2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
bindService(app, app2, null, null, 0, mock(IBinder.class));
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app, app2);
assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -2268,7 +2317,7 @@
bindService(app2, app3, null, null, 0, mock(IBinder.class));
app3.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
bindService(app3, app, null, null, 0, mock(IBinder.class));
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app, app2, app3);
assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -2313,7 +2362,7 @@
MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
app5.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
bindService(app, app5, null, s, 0, mock(IBinder.class));
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app, app2, app3, app4, app5);
assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -2355,7 +2404,7 @@
MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
app5.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
bindService(app, app5, null, s, 0, mock(IBinder.class));
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app5, app4, app3, app2, app);
assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -2397,7 +2446,7 @@
MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
app5.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
bindService(app, app5, null, s, 0, mock(IBinder.class));
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app3, app4, app2, app, app5);
assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -2437,7 +2486,7 @@
client3.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
bindService(app, client3, null, null, Context.BIND_INCLUDE_CAPABILITIES,
mock(IBinder.class));
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app, client, client2, client3);
final int expected = PROCESS_CAPABILITY_ALL & ~PROCESS_CAPABILITY_BFSL;
@@ -2468,7 +2517,7 @@
MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
app5.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
bindProvider(app, app5, cr, null, false);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app, app2, app3, app4, app5);
assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -2512,8 +2561,8 @@
doCallRealMethod().when(s).getConnections();
s.startRequested = true;
s.lastActivity = now;
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
- sService.mOomAdjuster.mNumServiceProcs = 3;
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mOomAdjuster.mNumServiceProcs = 3;
updateOomAdj(app3, app2, app);
assertEquals(SERVICE_B_ADJ, app3.mState.getSetAdj());
@@ -2530,15 +2579,15 @@
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
final int userOwner = 0;
final int userOther = 1;
- final int cachedAdj1 = sService.mConstants.USE_TIERED_CACHED_ADJ
+ final int cachedAdj1 = mService.mConstants.USE_TIERED_CACHED_ADJ
? CACHED_APP_MIN_ADJ + 10
: CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
- final int cachedAdj2 = sService.mConstants.USE_TIERED_CACHED_ADJ
+ final int cachedAdj2 = mService.mConstants.USE_TIERED_CACHED_ADJ
? CACHED_APP_MIN_ADJ + 10
: cachedAdj1 + ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
- doReturn(userOwner).when(sService.mUserController).getCurrentUserId();
+ doReturn(userOwner).when(mService.mUserController).getCurrentUserId();
- final ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
+ final ArrayList<ProcessRecord> lru = mService.mProcessList.getLruProcessesLOSP();
lru.clear();
lru.add(app2);
lru.add(app);
@@ -2549,10 +2598,10 @@
MOCKAPP2_PACKAGENAME + "/.TestService");
final long now = SystemClock.uptimeMillis();
- sService.mConstants.KEEP_WARMING_SERVICES.clear();
+ mService.mConstants.KEEP_WARMING_SERVICES.clear();
final ServiceInfo si = mock(ServiceInfo.class);
si.applicationInfo = mock(ApplicationInfo.class);
- ServiceRecord s = spy(new ServiceRecord(sService, cn, cn, null, 0, null,
+ ServiceRecord s = spy(new ServiceRecord(mService, cn, cn, null, 0, null,
si, false, null));
doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s).getConnections();
s.startRequested = true;
@@ -2564,16 +2613,16 @@
final ServiceInfo si2 = mock(ServiceInfo.class);
si2.applicationInfo = mock(ApplicationInfo.class);
si2.applicationInfo.uid = MOCKAPP2_UID_OTHER;
- ServiceRecord s2 = spy(new ServiceRecord(sService, cn2, cn2, null, 0, null,
+ ServiceRecord s2 = spy(new ServiceRecord(mService, cn2, cn2, null, 0, null,
si2, false, null));
doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s2).getConnections();
s2.startRequested = true;
- s2.lastActivity = now - sService.mConstants.MAX_SERVICE_INACTIVITY - 1;
+ s2.lastActivity = now - mService.mConstants.MAX_SERVICE_INACTIVITY - 1;
app2.mServices.startService(s2);
app2.mState.setHasShownUi(false);
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj();
assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-ui-services");
@@ -2590,7 +2639,7 @@
app.mState.setSetProcState(PROCESS_STATE_NONEXISTENT);
app.mState.setAdjType(null);
app.mState.setSetAdj(UNKNOWN_ADJ);
- s.lastActivity = now - sService.mConstants.MAX_SERVICE_INACTIVITY - 1;
+ s.lastActivity = now - mService.mConstants.MAX_SERVICE_INACTIVITY - 1;
updateOomAdj();
assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services");
@@ -2600,9 +2649,9 @@
app.mState.setAdjType(null);
app.mState.setSetAdj(UNKNOWN_ADJ);
app.mState.setHasShownUi(true);
- sService.mConstants.KEEP_WARMING_SERVICES.add(cn);
- sService.mConstants.KEEP_WARMING_SERVICES.add(cn2);
- s = spy(new ServiceRecord(sService, cn, cn, null, 0, null,
+ mService.mConstants.KEEP_WARMING_SERVICES.add(cn);
+ mService.mConstants.KEEP_WARMING_SERVICES.add(cn2);
+ s = spy(new ServiceRecord(mService, cn, cn, null, 0, null,
si, false, null));
doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s).getConnections();
s.startRequested = true;
@@ -2618,14 +2667,14 @@
app.mState.setAdjType(null);
app.mState.setSetAdj(UNKNOWN_ADJ);
app.mState.setHasShownUi(false);
- s.lastActivity = now - sService.mConstants.MAX_SERVICE_INACTIVITY - 1;
+ s.lastActivity = now - mService.mConstants.MAX_SERVICE_INACTIVITY - 1;
updateOomAdj();
assertProcStates(app, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services");
assertProcStates(app2, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services");
- doReturn(userOther).when(sService.mUserController).getCurrentUserId();
- sService.mOomAdjuster.handleUserSwitchedLocked();
+ doReturn(userOther).when(mService.mUserController).getCurrentUserId();
+ mService.mOomAdjuster.handleUserSwitchedLocked();
updateOomAdj();
assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services");
@@ -2637,9 +2686,9 @@
public void testUpdateOomAdj_DoOne_AboveClient_SameProcess() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
- doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
- doReturn(app).when(sService).getTopApp();
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
+ doReturn(app).when(mService).getTopApp();
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj());
@@ -2672,8 +2721,8 @@
s = bindService(app3, app2, null, null, 0, mock(IBinder.class));
s.lastActivity = now;
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
- sService.mOomAdjuster.mNumServiceProcs = 3;
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mOomAdjuster.mNumServiceProcs = 3;
updateOomAdj(app, app2, app3);
assertEquals(SERVICE_ADJ, app.mState.getSetAdj());
@@ -2688,9 +2737,9 @@
public void testUpdateOomAdj_DoOne_AboveClient_NotStarted() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
- doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
- doReturn(app).when(sService).getTopApp();
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
+ doReturn(app).when(mService).getTopApp();
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj());
@@ -2718,7 +2767,7 @@
ServiceRecord s = makeServiceRecord(app);
s.startRequested = true;
s.lastActivity = SystemClock.uptimeMillis();
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj();
assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND);
@@ -2738,7 +2787,7 @@
ServiceRecord s = makeServiceRecord(app);
s.startRequested = true;
s.lastActivity = SystemClock.uptimeMillis();
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdjPending(app);
assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND);
@@ -2760,7 +2809,7 @@
ServiceRecord s = makeServiceRecord(app);
s.startRequested = true;
s.lastActivity = SystemClock.uptimeMillis();
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj();
assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND);
@@ -2781,7 +2830,7 @@
ServiceRecord s = makeServiceRecord(app);
s.startRequested = true;
s.lastActivity = SystemClock.uptimeMillis();
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdjPending(app);
assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND);
@@ -2809,7 +2858,7 @@
client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
attributedClient.mServices.setHasForegroundServices(true, 0, true);
bindService(sandboxService, client, attributedClient, null, 0, mock(IBinder.class));
- sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj();
assertProcStates(client, PROCESS_STATE_PERSISTENT, PERSISTENT_PROC_ADJ,
SCHED_GROUP_DEFAULT);
@@ -2830,13 +2879,13 @@
// App1 binds to app2 and gets temp allowlisted.
bindService(app2, app, null, null, 0, mock(IBinder.class));
- sService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, true);
+ mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, true);
assertEquals(true, app.getUidRecord().isSetAllowListed());
assertEquals(true, app.mOptRecord.shouldNotFreeze());
assertEquals(true, app2.mOptRecord.shouldNotFreeze());
- sService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, false);
+ mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, false);
assertEquals(false, app.getUidRecord().isSetAllowListed());
assertEquals(false, app.mOptRecord.shouldNotFreeze());
assertEquals(false, app2.mOptRecord.shouldNotFreeze());
@@ -2856,8 +2905,8 @@
// App1 and app2 both bind to app3 and get temp allowlisted.
bindService(app3, app, null, null, 0, mock(IBinder.class));
bindService(app3, app2, null, null, 0, mock(IBinder.class));
- sService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, true);
- sService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP2_UID, true);
+ mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, true);
+ mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP2_UID, true);
assertEquals(true, app.getUidRecord().isSetAllowListed());
assertEquals(true, app2.getUidRecord().isSetAllowListed());
@@ -2866,7 +2915,7 @@
assertEquals(true, app3.mOptRecord.shouldNotFreeze());
// Remove app1 from allowlist.
- sService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, false);
+ mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, false);
assertEquals(false, app.getUidRecord().isSetAllowListed());
assertEquals(true, app2.getUidRecord().isSetAllowListed());
assertEquals(false, app.mOptRecord.shouldNotFreeze());
@@ -2874,7 +2923,7 @@
assertEquals(true, app3.mOptRecord.shouldNotFreeze());
// Now remove app2 from allowlist.
- sService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP2_UID, false);
+ mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP2_UID, false);
assertEquals(false, app.getUidRecord().isSetAllowListed());
assertEquals(false, app2.getUidRecord().isSetAllowListed());
assertEquals(false, app.mOptRecord.shouldNotFreeze());
@@ -2882,6 +2931,73 @@
assertEquals(false, app3.mOptRecord.shouldNotFreeze());
}
+ @SuppressWarnings("GuardedBy")
+ @Test
+ public void testUpdateOomAdj_DoAll_ClientlessService() {
+ ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+ MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
+
+ setProcessesToLru(app);
+ ServiceRecord s = makeServiceRecord(app);
+ s.startRequested = true;
+ s.lastActivity = SystemClock.uptimeMillis();
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ updateOomAdj();
+ assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND,
+ "started-services");
+
+ if (!Flags.followUpOomadjUpdates()) return;
+
+ final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class);
+ verify(mService.mHandler).sendEmptyMessageAtTime(
+ eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture());
+ mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
+ mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
+
+ assertProcStates(app, PROCESS_STATE_SERVICE, sFirstCachedAdj, SCHED_GROUP_BACKGROUND,
+ "cch-started-services");
+ // Follow up should not have been called again.
+ verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG),
+ followUpTimeCaptor.capture());
+ }
+
+ @SuppressWarnings("GuardedBy")
+ @Test
+ public void testUpdateOomAdj_DoAll_Multiple_Provider_Retention() {
+ ProcessRecord app1 = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+ MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
+ ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
+ MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
+ app1.mProviders.setLastProviderTime(SystemClock.uptimeMillis());
+ app2.mProviders.setLastProviderTime(SystemClock.uptimeMillis() + 2000);
+ mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ setProcessesToLru(app1, app2);
+ mService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE);
+
+ assertProcStates(app1, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ,
+ SCHED_GROUP_BACKGROUND, "recent-provider");
+ assertProcStates(app2, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ,
+ SCHED_GROUP_BACKGROUND, "recent-provider");
+
+ if (!Flags.followUpOomadjUpdates()) return;
+
+ final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class);
+ verify(mService.mHandler, atLeastOnce()).sendEmptyMessageAtTime(
+ eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture());
+ mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
+ mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
+
+ assertProcStates(app1, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND,
+ "cch-empty");
+
+ verify(mService.mHandler, atLeastOnce()).sendEmptyMessageAtTime(
+ eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture());
+ mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
+ mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
+ assertProcStates(app2, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND,
+ "cch-empty");
+ }
+
private ProcessRecord makeDefaultProcessRecord(int pid, int uid, String processName,
String packageName, boolean hasShownUi) {
return new ProcessRecordBuilder(pid, uid, processName, packageName).setHasShownUi(
@@ -2904,7 +3020,7 @@
}
private void setServiceMap(ServiceRecord s, int uid, ComponentName cn) {
- ActiveServices.ServiceMap serviceMap = sService.mServices.mServiceMap.get(
+ ActiveServices.ServiceMap serviceMap = mService.mServices.mServiceMap.get(
UserHandle.getUserId(uid));
if (serviceMap == null) {
serviceMap = mock(ActiveServices.ServiceMap.class);
@@ -2916,7 +3032,7 @@
new ArrayMap<>());
setFieldValue(ActiveServices.ServiceMap.class, serviceMap, "mDelayedStartList",
new ArrayList<>());
- sService.mServices.mServiceMap.put(UserHandle.getUserId(uid), serviceMap);
+ mService.mServices.mServiceMap.put(UserHandle.getUserId(uid), serviceMap);
}
serviceMap.mServicesByInstanceName.put(cn, s);
}
@@ -2957,6 +3073,7 @@
return record;
}
+ @SuppressWarnings("GuardedBy")
private void assertProcStates(ProcessRecord app, int expectedProcState, int expectedAdj,
int expectedSchedGroup) {
final ProcessStateRecord state = app.mState;
@@ -2974,6 +3091,7 @@
}
}
+ @SuppressWarnings("GuardedBy")
private void assertProcStates(ProcessRecord app, boolean expectedCached,
int expectedProcState, int expectedAdj, String expectedAdjType) {
final ProcessStateRecord state = app.mState;
@@ -2992,7 +3110,26 @@
}
}
- private static class ProcessRecordBuilder {
+ @SuppressWarnings("GuardedBy")
+ private void assertProcStates(ProcessRecord app, int expectedProcState, int expectedAdj,
+ int expectedSchedGroup, String expectedAdjType) {
+ final ProcessStateRecord state = app.mState;
+ assertEquals(expectedAdjType, state.getAdjType());
+ assertEquals(expectedProcState, state.getSetProcState());
+ assertEquals(expectedAdj, state.getSetAdj());
+ assertEquals(expectedSchedGroup, state.getSetSchedGroup());
+
+ // Below BFGS should never have BFSL.
+ if (expectedProcState > PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
+ assertNoBfsl(app);
+ }
+ // Above FGS should always have BFSL.
+ if (expectedProcState < PROCESS_STATE_FOREGROUND_SERVICE) {
+ assertBfsl(app);
+ }
+ }
+
+ private class ProcessRecordBuilder {
@SuppressWarnings("UnusedVariable")
int mPid;
int mUid;
@@ -3069,17 +3206,17 @@
ai.packageName = mPackageName;
ai.longVersionCode = mVersionCode;
ai.targetSdkVersion = mTargetSdkVersion;
- doCallRealMethod().when(sService).getPackageManagerInternal();
- doReturn(null).when(sPackageManagerInternal).getApplicationInfo(
+ doCallRealMethod().when(mService).getPackageManagerInternal();
+ doReturn(null).when(mPackageManagerInternal).getApplicationInfo(
eq(mSdkSandboxClientAppPackage), anyLong(), anyInt(), anyInt());
- ProcessRecord app = new ProcessRecord(sService, ai, mProcessName, mUid,
+ ProcessRecord app = new ProcessRecord(mService, ai, mProcessName, mUid,
mSdkSandboxClientAppPackage, -1, null);
final ProcessStateRecord state = app.mState;
final ProcessServiceRecord services = app.mServices;
final ProcessReceiverRecord receivers = app.mReceivers;
final ProcessProfileRecord profile = app.mProfile;
final ProcessProviderRecord providers = app.mProviders;
- app.makeActive(mock(IApplicationThread.class), sService.mProcessStats);
+ app.makeActive(mock(IApplicationThread.class), mService.mProcessStats);
app.setLastActivityTime(mLastActivityTime);
app.setKilledByAm(mKilledByAm);
app.setIsolatedEntryPoint(mIsolatedEntryPoint);
@@ -3124,14 +3261,35 @@
}
providers.setLastProviderTime(mLastProviderTime);
- UidRecord uidRec = sService.mOomAdjuster.mActiveUids.get(mUid);
+ UidRecord uidRec = mService.mOomAdjuster.mActiveUids.get(mUid);
if (uidRec == null) {
- uidRec = new UidRecord(mUid, sService);
- sService.mOomAdjuster.mActiveUids.put(mUid, uidRec);
+ uidRec = new UidRecord(mUid, mService);
+ mService.mOomAdjuster.mActiveUids.put(mUid, uidRec);
}
uidRec.addProcess(app);
app.setUidRecord(uidRec);
return app;
}
}
+
+ static class OomAdjusterInjector extends OomAdjuster.Injector {
+ // Jump ahead in time by this offset amount.
+ long mTimeOffsetMillis = 0;
+
+ void jumpUptimeAheadTo(long uptimeMillis) {
+ final long jumpMs = uptimeMillis - getUptimeMillis();
+ if (jumpMs <= 0) return;
+ mTimeOffsetMillis += jumpMs;
+ }
+
+ @Override
+ long getUptimeMillis() {
+ return SystemClock.uptimeMillis() + mTimeOffsetMillis;
+ }
+
+ @Override
+ long getElapsedRealtimeMillis() {
+ return SystemClock.elapsedRealtime() + mTimeOffsetMillis;
+ }
+ }
}
diff --git a/services/tests/ondeviceintelligencetests/OWNERS b/services/tests/ondeviceintelligencetests/OWNERS
new file mode 100644
index 0000000..09774f7
--- /dev/null
+++ b/services/tests/ondeviceintelligencetests/OWNERS
@@ -0,0 +1 @@
+file:/core/java/android/app/ondeviceintelligence/OWNERS
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
index 976cc18..a3f0770 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
@@ -91,7 +91,7 @@
final Parcel parcel = Parcel.obtain();
parcel.writeParcelable(outBatteryUsageStats, 0);
- assertThat(parcel.dataSize()).isLessThan(10000);
+ assertThat(parcel.dataSize()).isLessThan(12000);
parcel.setDataPosition(0);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerStatsTest.java
new file mode 100644
index 0000000..36deb08
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerStatsTest.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
+
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.hardware.power.stats.EnergyConsumerType;
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.Handler;
+import android.os.Process;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import com.android.internal.os.Clock;
+import com.android.internal.os.MonotonicClock;
+import com.android.internal.os.PowerProfile;
+import com.android.internal.os.PowerStats;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.function.IntSupplier;
+
+public class CameraPowerStatsTest {
+ @Rule(order = 0)
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+ .setProvideMainThread(true)
+ .build();
+
+ @Rule(order = 1)
+ public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+ .setAveragePower(PowerProfile.POWER_CAMERA, 100.0)
+ .initMeasuredEnergyStatsLocked();
+
+ private static final double PRECISION = 0.00001;
+ private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
+ private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
+ private static final int VOLTAGE_MV = 3500;
+ private static final int ENERGY_CONSUMER_ID = 777;
+
+ private final PowerStatsUidResolver mUidResolver = new PowerStatsUidResolver();
+ @Mock
+ private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
+
+ EnergyConsumerPowerStatsCollector.Injector mInjector =
+ new EnergyConsumerPowerStatsCollector.Injector() {
+ @Override
+ public Handler getHandler() {
+ return mStatsRule.getHandler();
+ }
+
+ @Override
+ public Clock getClock() {
+ return mStatsRule.getMockClock();
+ }
+
+ @Override
+ public PowerStatsUidResolver getUidResolver() {
+ return mUidResolver;
+ }
+
+ @Override
+ public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
+ return 0;
+ }
+
+ @Override
+ public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
+ return mConsumedEnergyRetriever;
+ }
+
+ @Override
+ public IntSupplier getVoltageSupplier() {
+ return () -> VOLTAGE_MV;
+ }
+ };
+
+ private MonotonicClock mMonotonicClock;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mMonotonicClock = new MonotonicClock(0, mStatsRule.getMockClock());
+ }
+
+ @Test
+ public void energyConsumerModel() {
+ when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.CAMERA, null))
+ .thenReturn(new int[]{ENERGY_CONSUMER_ID});
+ CameraPowerStatsProcessor processor = new CameraPowerStatsProcessor(
+ mStatsRule.getPowerProfile(), mUidResolver);
+
+ PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(processor);
+
+ CameraPowerStatsCollector collector = new CameraPowerStatsCollector(mInjector);
+ collector.addConsumer(
+ powerStats -> {
+ processor.addPowerStats(stats, powerStats, mMonotonicClock.monotonicTime());
+ });
+ collector.setEnabled(true);
+
+ // Establish a baseline
+ when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{ENERGY_CONSUMER_ID}))
+ .thenReturn(new long[]{uCtoUj(10000)});
+ collector.collectAndDeliverStats();
+
+ processor.noteStateChange(stats, buildHistoryItem(0, true, APP_UID1));
+
+ // Turn the screen off after 2.5 seconds
+ stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
+ stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
+ stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE, 5000);
+
+ processor.noteStateChange(stats, buildHistoryItem(6000, false, APP_UID1));
+
+ when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{ENERGY_CONSUMER_ID}))
+ .thenReturn(new long[]{uCtoUj(2_170_000)});
+ collector.collectAndDeliverStats();
+
+ processor.noteStateChange(stats, buildHistoryItem(7000, true, APP_UID2));
+
+ mStatsRule.setTime(11_000, 11_000);
+ when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{ENERGY_CONSUMER_ID}))
+ .thenReturn(new long[]{uCtoUj(3_610_000)});
+ collector.collectAndDeliverStats();
+
+ processor.finish(stats, 11_000);
+
+ PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor();
+ BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout();
+ statsLayout.fromExtras(descriptor.extras);
+
+ // Total estimated power = 3,600,000 uC = 1.0 mAh
+ // of which 3,000,000 is distributed:
+ // Screen-on - 2500/6000 * 2160000 = 900000 uC = 0.25 mAh
+ // Screen-off - 3500/6000 * 2160000 = 1260000 uC = 0.35 mAh
+ // and 600,000 was fully with screen off:
+ // Screen-off - 1440000 uC = 0.4 mAh
+ long[] deviceStats = new long[descriptor.statsArrayLength];
+ stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+ assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+ .isWithin(PRECISION).of(0.25);
+
+ stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
+ assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+ .isWithin(PRECISION).of(0.35 + 0.4);
+
+ // UID1 =
+ // 2,160,000 uC = 0.6 mAh
+ // split between three different states
+ // fg screen-on: 2500/6000
+ // bg screen-off: 2500/6000
+ // fgs screen-off: 1000/6000
+ double expectedPower1 = 0.6;
+ long[] uidStats = new long[descriptor.uidStatsArrayLength];
+ stats.getUidStats(uidStats, APP_UID1,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000);
+
+
+ stats.getUidStats(uidStats, APP_UID1,
+ states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000);
+
+ stats.getUidStats(uidStats, APP_UID1,
+ states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(expectedPower1 * 1000 / 6000);
+
+ // UID2 =
+ // 1440000 mA-ms = 0.4 mAh
+ // all in the same state
+ double expectedPower2 = 0.4;
+ stats.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(expectedPower2);
+
+ stats.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0);
+ }
+
+ private BatteryStats.HistoryItem buildHistoryItem(int timestamp, boolean stateOn,
+ int uid) {
+ mStatsRule.setTime(timestamp, timestamp);
+ BatteryStats.HistoryItem historyItem = new BatteryStats.HistoryItem();
+ historyItem.time = mMonotonicClock.monotonicTime();
+ historyItem.states2 = stateOn ? BatteryStats.HistoryItem.STATE2_CAMERA_FLAG : 0;
+ if (stateOn) {
+ historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
+ | BatteryStats.HistoryItem.EVENT_FLAG_START;
+ } else {
+ historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
+ | BatteryStats.HistoryItem.EVENT_FLAG_FINISH;
+ }
+ historyItem.eventTag = historyItem.localEventTag;
+ historyItem.eventTag.uid = uid;
+ historyItem.eventTag.string = "camera";
+ return historyItem;
+ }
+
+ private int[] states(int... states) {
+ return states;
+ }
+
+ private static PowerComponentAggregatedPowerStats createAggregatedPowerStats(
+ BinaryStatePowerStatsProcessor processor) {
+ AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_CAMERA)
+ .trackDeviceStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
+ .trackUidStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+ .setProcessor(processor);
+
+ AggregatedPowerStats aggregatedPowerStats = new AggregatedPowerStats(config);
+ PowerComponentAggregatedPowerStats powerComponentStats =
+ aggregatedPowerStats.getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_CAMERA);
+ processor.start(powerComponentStats, 0);
+
+ powerComponentStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
+ powerComponentStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
+ powerComponentStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
+ powerComponentStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
+
+ return powerComponentStats;
+ }
+
+ private static long uCtoUj(long uc) {
+ return (long) (uc * (double) VOLTAGE_MV / 1000);
+ }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerStatsTest.java
new file mode 100644
index 0000000..8a391c6
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerStatsTest.java
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
+
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.hardware.power.stats.EnergyConsumerType;
+import android.location.GnssSignalQuality;
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.Handler;
+import android.os.Process;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import com.android.internal.os.Clock;
+import com.android.internal.os.MonotonicClock;
+import com.android.internal.os.PowerProfile;
+import com.android.internal.os.PowerStats;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.function.IntSupplier;
+
+public class GnssPowerStatsTest {
+ @Rule(order = 0)
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+ .setProvideMainThread(true)
+ .build();
+
+ @Rule(order = 1)
+ public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+ .setAveragePower(PowerProfile.POWER_GPS_ON, 100.0)
+ .setAveragePower(PowerProfile.POWER_GPS_SIGNAL_QUALITY_BASED, new double[]{1000, 100})
+ .initMeasuredEnergyStatsLocked();
+
+ private static final double PRECISION = 0.00001;
+ private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
+ private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
+ private static final int VOLTAGE_MV = 3500;
+ private static final int ENERGY_CONSUMER_ID = 777;
+
+ private final PowerStatsUidResolver mUidResolver = new PowerStatsUidResolver();
+ @Mock
+ private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
+
+ EnergyConsumerPowerStatsCollector.Injector mInjector =
+ new EnergyConsumerPowerStatsCollector.Injector() {
+ @Override
+ public Handler getHandler() {
+ return mStatsRule.getHandler();
+ }
+
+ @Override
+ public Clock getClock() {
+ return mStatsRule.getMockClock();
+ }
+
+ @Override
+ public PowerStatsUidResolver getUidResolver() {
+ return mUidResolver;
+ }
+
+ @Override
+ public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
+ return 0;
+ }
+
+ @Override
+ public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
+ return mConsumedEnergyRetriever;
+ }
+
+ @Override
+ public IntSupplier getVoltageSupplier() {
+ return () -> VOLTAGE_MV;
+ }
+ };
+
+ private MonotonicClock mMonotonicClock;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mMonotonicClock = new MonotonicClock(0, mStatsRule.getMockClock());
+ }
+
+ @Test
+ public void powerProfileModel() {
+ // ODPM unsupported
+ when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.GNSS, null))
+ .thenReturn(new int[0]);
+ GnssPowerStatsProcessor processor = new GnssPowerStatsProcessor(
+ mStatsRule.getPowerProfile(), mUidResolver);
+
+ PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(processor);
+
+ GnssPowerStatsCollector collector = new GnssPowerStatsCollector(mInjector);
+ collector.addConsumer(
+ powerStats -> {
+ processor.addPowerStats(stats, powerStats, mMonotonicClock.monotonicTime());
+ });
+ collector.setEnabled(true);
+
+ // Establish a baseline
+ collector.collectAndDeliverStats();
+
+ processor.noteStateChange(stats, buildHistoryItem(0, true, APP_UID1));
+
+ // Turn the screen off after 2.5 seconds
+ stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
+ stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
+ stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE, 5000);
+
+ processor.noteStateChange(stats, buildHistoryItem(6000, false, APP_UID1));
+
+ collector.collectAndDeliverStats();
+
+ processor.noteStateChange(stats, buildHistoryItem(7000, true, APP_UID2));
+ processor.noteStateChange(stats, buildHistoryItem(7000,
+ GnssSignalQuality.GNSS_SIGNAL_QUALITY_GOOD));
+ processor.noteStateChange(stats, buildHistoryItem(8000,
+ GnssSignalQuality.GNSS_SIGNAL_QUALITY_POOR));
+ mStatsRule.setTime(11_000, 11_000);
+ collector.collectAndDeliverStats();
+
+ processor.finish(stats, 11_000);
+
+ PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor();
+ BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout();
+ statsLayout.fromExtras(descriptor.extras);
+
+ // scr-on, GNSS-good: 2500 * 100 = 250000 mA-ms = 0.06944 mAh
+ // scr-off GNSS=good: 4500 * 100 = 0.12500 mAh
+ // scr-off GNSS=poor: 3000 * 1000 = 0.83333 mAh
+ // scr-off GNSS-on: 0.12500 + 0.83333 = 0.95833 mAh
+ long[] deviceStats = new long[descriptor.statsArrayLength];
+ stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+ assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+ .isWithin(PRECISION).of(0.06944);
+
+ stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
+ assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+ .isWithin(PRECISION).of(0.12500 + 0.83333);
+
+ // UID1 =
+ // scr-on FG: 2500 -> 0.06944 mAh
+ // scr-off BG: 2500/7500 * 0.95833 = 0.31944 mAh
+ // scr-off FGS: 1000/7500 * 0.95833 = 0.12777 mAh
+ long[] uidStats = new long[descriptor.uidStatsArrayLength];
+ stats.getUidStats(uidStats, APP_UID1,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0.06944);
+
+ stats.getUidStats(uidStats, APP_UID1,
+ states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0.31944);
+
+ stats.getUidStats(uidStats, APP_UID1,
+ states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0.12777);
+
+ // UID2 =
+ // scr-off cached: 4000/7500 * 0.95833 = 0.51111 mAh
+ stats.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0.51111);
+
+ stats.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0);
+ }
+
+ @Test
+ public void energyConsumerModel() {
+ when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.GNSS, null))
+ .thenReturn(new int[]{ENERGY_CONSUMER_ID});
+ GnssPowerStatsProcessor processor = new GnssPowerStatsProcessor(
+ mStatsRule.getPowerProfile(), mUidResolver);
+
+ PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(processor);
+
+ GnssPowerStatsCollector collector = new GnssPowerStatsCollector(mInjector);
+ collector.addConsumer(
+ powerStats -> {
+ processor.addPowerStats(stats, powerStats, mMonotonicClock.monotonicTime());
+ });
+ collector.setEnabled(true);
+
+ // Establish a baseline
+ when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{ENERGY_CONSUMER_ID}))
+ .thenReturn(new long[]{uCtoUj(10000)});
+ collector.collectAndDeliverStats();
+
+ processor.noteStateChange(stats, buildHistoryItem(0, true, APP_UID1));
+
+ // Turn the screen off after 2.5 seconds
+ stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
+ stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
+ stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE, 5000);
+
+ processor.noteStateChange(stats, buildHistoryItem(6000, false, APP_UID1));
+
+ when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{ENERGY_CONSUMER_ID}))
+ .thenReturn(new long[]{uCtoUj(2_170_000)});
+ collector.collectAndDeliverStats();
+
+ processor.noteStateChange(stats, buildHistoryItem(7000, true, APP_UID2));
+ processor.noteStateChange(stats, buildHistoryItem(7000,
+ GnssSignalQuality.GNSS_SIGNAL_QUALITY_GOOD));
+ processor.noteStateChange(stats, buildHistoryItem(8000,
+ GnssSignalQuality.GNSS_SIGNAL_QUALITY_POOR));
+ mStatsRule.setTime(11_000, 11_000);
+ when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{ENERGY_CONSUMER_ID}))
+ .thenReturn(new long[]{uCtoUj(3_610_000)});
+ collector.collectAndDeliverStats();
+
+ processor.finish(stats, 11_000);
+
+ PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor();
+ BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout();
+ statsLayout.fromExtras(descriptor.extras);
+
+ // Total estimated power = 3,600,000 uC = 1.0 mAh
+ // of which 3,000,000 is distributed:
+ // Screen-on - 2500/6000 * 2160000 = 900000 uC = 0.25 mAh
+ // Screen-off - 3500/6000 * 2160000 = 1260000 uC = 0.35 mAh
+ // and 600,000 was fully with screen off:
+ // Screen-off - 1440000 uC = 0.4 mAh
+ long[] deviceStats = new long[descriptor.statsArrayLength];
+ stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+ assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+ .isWithin(PRECISION).of(0.25);
+
+ stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
+ assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+ .isWithin(PRECISION).of(0.35 + 0.4);
+
+ // UID1 =
+ // 2,160,000 uC = 0.6 mAh
+ // split between three different states
+ // fg screen-on: 2500/6000
+ // bg screen-off: 2500/6000
+ // fgs screen-off: 1000/6000
+ double expectedPower1 = 0.6;
+ long[] uidStats = new long[descriptor.uidStatsArrayLength];
+ stats.getUidStats(uidStats, APP_UID1,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000);
+
+ stats.getUidStats(uidStats, APP_UID1,
+ states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000);
+
+ stats.getUidStats(uidStats, APP_UID1,
+ states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(expectedPower1 * 1000 / 6000);
+
+ // UID2 =
+ // 1440000 mA-ms = 0.4 mAh
+ // all in the same state
+ double expectedPower2 = 0.4;
+ stats.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(expectedPower2);
+
+ stats.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0);
+ }
+
+ private BatteryStats.HistoryItem buildHistoryItem(int timestamp, boolean stateOn,
+ int uid) {
+ mStatsRule.setTime(timestamp, timestamp);
+ BatteryStats.HistoryItem historyItem = new BatteryStats.HistoryItem();
+ historyItem.time = mMonotonicClock.monotonicTime();
+ historyItem.states = stateOn ? BatteryStats.HistoryItem.STATE_GPS_ON_FLAG : 0;
+ if (stateOn) {
+ historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
+ | BatteryStats.HistoryItem.EVENT_FLAG_START;
+ } else {
+ historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
+ | BatteryStats.HistoryItem.EVENT_FLAG_FINISH;
+ }
+ historyItem.eventTag = historyItem.localEventTag;
+ historyItem.eventTag.uid = uid;
+ historyItem.eventTag.string = "gnss";
+ return historyItem;
+ }
+
+ private BatteryStats.HistoryItem buildHistoryItem(int timestamp, int signalLevel) {
+ mStatsRule.setTime(timestamp, timestamp);
+ BatteryStats.HistoryItem historyItem = new BatteryStats.HistoryItem();
+ historyItem.time = mMonotonicClock.monotonicTime();
+ historyItem.states = BatteryStats.HistoryItem.STATE_GPS_ON_FLAG;
+ historyItem.states2 =
+ signalLevel << BatteryStats.HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT;
+ return historyItem;
+ }
+
+ private int[] states(int... states) {
+ return states;
+ }
+
+ private static PowerComponentAggregatedPowerStats createAggregatedPowerStats(
+ BinaryStatePowerStatsProcessor processor) {
+ AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_GNSS)
+ .trackDeviceStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
+ .trackUidStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+ .setProcessor(processor);
+
+ AggregatedPowerStats aggregatedPowerStats = new AggregatedPowerStats(config);
+ PowerComponentAggregatedPowerStats powerComponentStats =
+ aggregatedPowerStats.getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_GNSS);
+ processor.start(powerComponentStats, 0);
+
+ powerComponentStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
+ powerComponentStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
+ powerComponentStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
+ powerComponentStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
+
+ return powerComponentStats;
+ }
+
+ private static long uCtoUj(long uc) {
+ return (long) (uc * (double) VOLTAGE_MV / 1000);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java b/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java
index 8cbed2c..31bf5f0 100644
--- a/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java
@@ -61,20 +61,6 @@
private static final long USAGE_STATS_INTERACTION = 10 * 60 * 1000L;
private static final long SERVICE_USAGE_INTERACTION = 60 * 1000;
- static class MyOomAdjuster extends OomAdjuster {
-
- MyOomAdjuster(ActivityManagerService service, ProcessList processList,
- ActiveUids activeUids) {
- super(service, processList, activeUids);
- }
-
- @Override
- protected boolean isChangeEnabled(int changeId, ApplicationInfo app,
- boolean defaultValue) {
- return true;
- }
- }
-
@BeforeClass
public static void setUpOnce() {
sContext = getInstrumentation().getTargetContext();
@@ -99,7 +85,15 @@
final AppProfiler profiler = mock(AppProfiler.class);
setFieldValue(AppProfiler.class, profiler, "mProfilerLock", new Object());
setFieldValue(ActivityManagerService.class, sService, "mAppProfiler", profiler);
- sService.mOomAdjuster = new MyOomAdjuster(sService, sService.mProcessList, null);
+ final OomAdjuster.Injector injector = new OomAdjuster.Injector(){
+ @Override
+ boolean isChangeEnabled(int changeId, ApplicationInfo app,
+ boolean defaultValue) {
+ return true;
+ }
+ };
+ sService.mOomAdjuster = new OomAdjuster(sService, sService.mProcessList, null,
+ injector);
LocalServices.addService(UsageStatsManagerInternal.class,
mock(UsageStatsManagerInternal.class));
sService.mUsageStatsService = LocalServices.getService(UsageStatsManagerInternal.class);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
index 40de1b2..182d603 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
@@ -320,7 +320,7 @@
}
@Test
- public void luxProbeNotEnabledOnStartWhenNotWake() throws RemoteException {
+ public void luxProbeDisabledOnStartWhenNotWake() throws RemoteException {
luxProbeEnabledOnStart(false /* isAwake */);
}
@@ -337,6 +337,7 @@
.getValue().toAidlContext());
verify(mLuxProbe, isAwake ? times(1) : never()).enable();
+ verify(mLuxProbe, isAwake ? never() : times(1)).disable();
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageTest.java
old mode 100755
new mode 100644
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 3cab75b..3d68849 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -18,11 +18,13 @@
import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
import static android.Manifest.permission.NETWORK_STACK;
+import static android.app.ActivityManager.MAX_PROCESS_STATE;
import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL;
import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE;
import static android.app.ActivityManager.PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK;
import static android.app.ActivityManager.PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK;
import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
import static android.app.ActivityManager.PROCESS_STATE_SERVICE;
import static android.app.ActivityManager.PROCESS_STATE_TOP;
import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER;
@@ -165,9 +167,11 @@
import android.os.PowerSaveState;
import android.os.RemoteException;
import android.os.SimpleClock;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
@@ -2158,7 +2162,8 @@
@Test
@RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
- public void testBackgroundChainOnProcStateChange() throws Exception {
+ @RequiresFlagsDisabled(Flags.FLAG_USE_DIFFERENT_DELAYS_FOR_BACKGROUND_CHAIN)
+ public void testBackgroundChainOnProcStateChangeSameDelay() throws Exception {
// initialization calls setFirewallChainEnabled, so we want to reset the invocations.
clearInvocations(mNetworkManager);
@@ -2186,6 +2191,59 @@
}
@Test
+ @RequiresFlagsEnabled({
+ Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE,
+ Flags.FLAG_USE_DIFFERENT_DELAYS_FOR_BACKGROUND_CHAIN
+ })
+ public void testBackgroundChainOnProcStateChangeDifferentDelays() throws Exception {
+ // The app will be blocked when there is no prior proc-state.
+ assertTrue(mService.isUidNetworkingBlocked(UID_A, false));
+
+ // Tweak delays to avoid waiting too long in tests.
+ mService.mBackgroundRestrictionShortDelayMs = 50;
+ mService.mBackgroundRestrictionLongDelayMs = 1000;
+
+ int procStateSeq = 231; // Any arbitrary starting sequence.
+ for (int ps = BACKGROUND_THRESHOLD_STATE; ps <= MAX_PROCESS_STATE; ps++) {
+ clearInvocations(mNetworkManager);
+
+ // Make sure app is in correct process-state to access network.
+ callAndWaitOnUidStateChanged(UID_A, BACKGROUND_THRESHOLD_STATE - 1, procStateSeq++);
+ verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, UID_A,
+ FIREWALL_RULE_ALLOW);
+ assertFalse(mService.isUidNetworkingBlocked(UID_A, false));
+
+ // Now put the app into the background and test that it eventually loses network.
+ callAndWaitOnUidStateChanged(UID_A, ps, procStateSeq++);
+
+ final long uidStateChangeTime = SystemClock.uptimeMillis();
+ if (ps <= PROCESS_STATE_LAST_ACTIVITY) {
+ // Verify that the app is blocked after long delay but not after short delay.
+ waitForDelayedMessageOnHandler(mService.mBackgroundRestrictionShortDelayMs + 1);
+ verify(mNetworkManager, never()).setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND,
+ UID_A, FIREWALL_RULE_DEFAULT);
+ assertFalse(mService.isUidNetworkingBlocked(UID_A, false));
+
+ final long timeUntilLongDelay = uidStateChangeTime
+ + mService.mBackgroundRestrictionLongDelayMs - SystemClock.uptimeMillis();
+ assertTrue("No time left to verify long delay in background transition",
+ timeUntilLongDelay >= 0);
+
+ waitForDelayedMessageOnHandler(timeUntilLongDelay + 1);
+ verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, UID_A,
+ FIREWALL_RULE_DEFAULT);
+ assertTrue(mService.isUidNetworkingBlocked(UID_A, false));
+ } else {
+ // Verify that the app is blocked after short delay.
+ waitForDelayedMessageOnHandler(mService.mBackgroundRestrictionShortDelayMs + 1);
+ verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, UID_A,
+ FIREWALL_RULE_DEFAULT);
+ assertTrue(mService.isUidNetworkingBlocked(UID_A, false));
+ }
+ }
+ }
+
+ @Test
@RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
public void testBackgroundChainOnAllowlistChange() throws Exception {
// initialization calls setFirewallChainEnabled, so we want to reset the invocations.
@@ -2881,6 +2939,11 @@
}
}
+ /**
+ * This posts a blocking message to the service handler with the given delayMs and waits for it
+ * to complete. This ensures that all messages posted before the given delayMs will also
+ * have been executed before this method returns and can be verified in subsequent code.
+ */
private void waitForDelayedMessageOnHandler(long delayMs) throws InterruptedException {
final CountDownLatch latch = new CountDownLatch(1);
mService.getHandlerForTesting().postDelayed(latch::countDown, delayMs);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
old mode 100755
new mode 100644
index 15c9bfb..f07e5bc
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -511,7 +511,8 @@
TestableNotificationManagerService.StrongAuthTrackerFake mStrongAuthTracker;
TestableFlagResolver mTestFlagResolver = new TestableFlagResolver();
- @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake(
1 << 30);
@Mock
@@ -626,7 +627,7 @@
when(mPackageManagerClient.getPackageUidAsUser(any(), anyInt())).thenReturn(mUid);
when(mPackageManagerInternal.isSameApp(anyString(), anyInt(), anyInt())).thenAnswer(
(Answer<Boolean>) invocation -> {
- // TODO: b/317957802 - This is overly broad and basically makes ANY
+ // TODO: b/317957802 - This is overly broad and basically makes ANY
// isSameApp() check pass, requiring Mockito.reset() for meaningful
// tests! Make it more precise.
Object[] args = invocation.getArguments();
@@ -6892,22 +6893,15 @@
verify(visitor, times(1)).accept(eq(personIcon3.getUri()));
}
- private PendingIntent getPendingIntentWithUri(Uri uri) {
- return PendingIntent.getActivity(mContext, 0,
- new Intent("action", uri),
- PendingIntent.FLAG_IMMUTABLE);
- }
-
@Test
- @EnableFlags({android.app.Flags.FLAG_VISIT_RISKY_URIS})
- public void testVisitUris_callStyle_ongoingCall() {
+ public void testVisitUris_callStyle() {
Icon personIcon = Icon.createWithContentUri("content://media/person");
Icon verificationIcon = Icon.createWithContentUri("content://media/verification");
Person callingPerson = new Person.Builder().setName("Someone")
.setIcon(personIcon)
.build();
- Uri hangUpUri = Uri.parse("content://intent/hangup");
- PendingIntent hangUpIntent = getPendingIntentWithUri(hangUpUri);
+ PendingIntent hangUpIntent = PendingIntent.getActivity(mContext, 0, new Intent(),
+ PendingIntent.FLAG_IMMUTABLE);
Notification n = new Notification.Builder(mContext, "a")
.setStyle(Notification.CallStyle.forOngoingCall(callingPerson, hangUpIntent)
.setVerificationIcon(verificationIcon))
@@ -6920,42 +6914,10 @@
verify(visitor, times(1)).accept(eq(personIcon.getUri()));
verify(visitor, times(1)).accept(eq(verificationIcon.getUri()));
- verify(visitor, times(1)).accept(eq(hangUpUri));
hangUpIntent.cancel();
}
@Test
- @EnableFlags({android.app.Flags.FLAG_VISIT_RISKY_URIS})
- public void testVisitUris_callStyle_incomingCall() {
- Icon personIcon = Icon.createWithContentUri("content://media/person");
- Icon verificationIcon = Icon.createWithContentUri("content://media/verification");
- Person callingPerson = new Person.Builder().setName("Someone")
- .setIcon(personIcon)
- .build();
- Uri answerUri = Uri.parse("content://intent/answer");
- PendingIntent answerIntent = getPendingIntentWithUri(answerUri);
- Uri declineUri = Uri.parse("content://intent/decline");
- PendingIntent declineIntent = getPendingIntentWithUri(declineUri);
- Notification n = new Notification.Builder(mContext, "a")
- .setStyle(Notification.CallStyle.forIncomingCall(callingPerson, declineIntent,
- answerIntent)
- .setVerificationIcon(verificationIcon))
- .setContentTitle("Calling...")
- .setSmallIcon(android.R.drawable.sym_def_app_icon)
- .build();
-
- Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class);
- n.visitUris(visitor);
-
- verify(visitor, times(1)).accept(eq(personIcon.getUri()));
- verify(visitor, times(1)).accept(eq(verificationIcon.getUri()));
- verify(visitor, times(1)).accept(eq(answerIntent.getIntent().getData()));
- verify(visitor, times(1)).accept(eq(declineUri));
- answerIntent.cancel();
- declineIntent.cancel();
- }
-
- @Test
public void testVisitUris_styleExtrasWithoutStyle() {
Notification.Builder notification = new Notification.Builder(mContext, "a")
.setSmallIcon(android.R.drawable.sym_def_app_icon);
@@ -7001,87 +6963,23 @@
}
@Test
- @EnableFlags({android.app.Flags.FLAG_VISIT_RISKY_URIS})
public void testVisitUris_wearableExtender() {
Icon actionIcon = Icon.createWithContentUri("content://media/action");
Icon wearActionIcon = Icon.createWithContentUri("content://media/wearAction");
- Uri displayIntentUri = Uri.parse("content://intent/display");
- PendingIntent displayIntent = getPendingIntentWithUri(displayIntentUri);
- Uri actionIntentUri = Uri.parse("content://intent/action");
- PendingIntent actionIntent = getPendingIntentWithUri(actionIntentUri);
- Uri wearActionIntentUri = Uri.parse("content://intent/wear");
- PendingIntent wearActionIntent = getPendingIntentWithUri(wearActionIntentUri);
+ PendingIntent intent = PendingIntent.getActivity(mContext, 0, new Intent(),
+ PendingIntent.FLAG_IMMUTABLE);
Notification n = new Notification.Builder(mContext, "a")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
- .addAction(
- new Notification.Action.Builder(actionIcon, "Hey!", actionIntent).build())
- .extend(new Notification.WearableExtender()
- .setDisplayIntent(displayIntent)
- .addAction(new Notification.Action.Builder(wearActionIcon, "Wear!",
- wearActionIntent)
- .build()))
+ .addAction(new Notification.Action.Builder(actionIcon, "Hey!", intent).build())
+ .extend(new Notification.WearableExtender().addAction(
+ new Notification.Action.Builder(wearActionIcon, "Wear!", intent).build()))
.build();
Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class);
n.visitUris(visitor);
verify(visitor).accept(eq(actionIcon.getUri()));
- verify(visitor, times(1)).accept(eq(actionIntentUri));
verify(visitor).accept(eq(wearActionIcon.getUri()));
- verify(visitor, times(1)).accept(eq(wearActionIntentUri));
- displayIntent.cancel();
- actionIntent.cancel();
- wearActionIntent.cancel();
- }
-
- @Test
- @EnableFlags({android.app.Flags.FLAG_VISIT_RISKY_URIS})
- public void testVisitUris_tvExtender() {
- Uri contentIntentUri = Uri.parse("content://intent/content");
- PendingIntent contentIntent = getPendingIntentWithUri(contentIntentUri);
- Uri deleteIntentUri = Uri.parse("content://intent/delete");
- PendingIntent deleteIntent = getPendingIntentWithUri(deleteIntentUri);
- Notification n = new Notification.Builder(mContext, "a")
- .extend(
- new Notification.TvExtender()
- .setContentIntent(contentIntent)
- .setDeleteIntent(deleteIntent))
- .build();
-
- Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class);
- n.visitUris(visitor);
-
- verify(visitor, times(1)).accept(eq(contentIntentUri));
- verify(visitor, times(1)).accept(eq(deleteIntentUri));
- contentIntent.cancel();
- deleteIntent.cancel();
- }
-
- @Test
- @EnableFlags({android.app.Flags.FLAG_VISIT_RISKY_URIS})
- public void testVisitUris_carExtender() {
- final String testParticipant = "testParticipant";
- Uri readPendingIntentUri = Uri.parse("content://intent/read");
- PendingIntent readPendingIntent = getPendingIntentWithUri(readPendingIntentUri);
- Uri replyPendingIntentUri = Uri.parse("content://intent/reply");
- PendingIntent replyPendingIntent = getPendingIntentWithUri(replyPendingIntentUri);
- final RemoteInput testRemoteInput = new RemoteInput.Builder("key").build();
-
- Notification n = new Notification.Builder(mContext, "a")
- .extend(new Notification.CarExtender().setUnreadConversation(
- new Notification.CarExtender.Builder(testParticipant)
- .setReadPendingIntent(readPendingIntent)
- .setReplyAction(replyPendingIntent, testRemoteInput)
- .build()))
- .build();
-
- Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class);
- n.visitUris(visitor);
-
- verify(visitor, times(1)).accept(eq(readPendingIntentUri));
- verify(visitor, times(1)).accept(eq(replyPendingIntentUri));
- readPendingIntent.cancel();
- replyPendingIntent.cancel();
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java
index 863cda4..594d6f2 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java
@@ -34,6 +34,7 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.Log;
import android.view.LayoutInflater;
@@ -87,6 +88,7 @@
import javax.annotation.Nullable;
@RunWith(AndroidJUnit4.class)
+@EnableFlags(Flags.FLAG_VISIT_PERSON_URI)
public class NotificationVisitUrisTest extends UiServiceTestCase {
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -153,10 +155,6 @@
.put(Notification.Action.Builder.class, "extend")
// Overwrites icon supplied to constructor.
.put(Notification.BubbleMetadata.Builder.class, "setIcon")
- // Overwrites intent supplied to constructor.
- .put(Notification.BubbleMetadata.Builder.class, "setIntent")
- // Overwrites intent supplied to constructor.
- .put(Notification.BubbleMetadata.Builder.class, "setDeleteIntent")
// Discards previously-added actions.
.put(RemoteViews.class, "mergeRemoteViews")
.build();
@@ -172,7 +170,6 @@
@Before
public void setUp() {
mContext = InstrumentationRegistry.getInstrumentation().getContext();
- mSetFlagsRule.enableFlags(Flags.FLAG_VISIT_RISKY_URIS);
}
@After
@@ -700,14 +697,13 @@
}
if (clazz == Intent.class) {
- return new Intent("action", generateUri(where.plus(Intent.class)));
+ return new Intent("action");
}
if (clazz == PendingIntent.class) {
- // PendingIntent can have an Intent with a Uri.
- Uri intentUri = generateUri(where.plus(PendingIntent.class));
- return PendingIntent.getActivity(mContext, 0,
- new Intent("action", intentUri),
+ // PendingIntent can have an Intent with a Uri but those are inaccessible and
+ // not inspected.
+ return PendingIntent.getActivity(mContext, 0, new Intent("action"),
PendingIntent.FLAG_IMMUTABLE);
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index abfb95c..9352c12 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -17,7 +17,16 @@
package com.android.server.notification;
import static android.app.AutomaticZenRule.TYPE_BEDTIME;
+import static android.app.Flags.FLAG_MODES_UI;
+import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.provider.Settings.Global.ZEN_MODE_OFF;
+import static android.service.notification.Condition.SOURCE_USER_ACTION;
+import static android.service.notification.Condition.STATE_FALSE;
+import static android.service.notification.Condition.STATE_TRUE;
import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
+import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_NONE;
+import static android.service.notification.ZenPolicy.PEOPLE_TYPE_ANYONE;
+import static android.service.notification.ZenPolicy.STATE_ALLOW;
import static com.google.common.truth.Truth.assertThat;
@@ -33,6 +42,9 @@
import android.content.ComponentName;
import android.net.Uri;
import android.os.Parcel;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.FlagsParameterization;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.service.notification.Condition;
@@ -43,7 +55,6 @@
import android.util.Xml;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
@@ -63,9 +74,13 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.Instant;
+import java.util.List;
+
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(ParameterizedAndroidJunit4.class)
public class ZenModeConfigTest extends UiServiceTestCase {
private final String NAME = "name";
@@ -91,6 +106,16 @@
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(
SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT);
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return FlagsParameterization.allCombinationsOf(
+ FLAG_MODES_UI);
+ }
+
+ public ZenModeConfigTest(FlagsParameterization flags) {
+ mSetFlagsRule.setFlagsParameterization(flags);
+ }
+
@Before
public final void setUp() {
mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
@@ -101,13 +126,44 @@
ZenModeConfig config = getMutedRingerConfig();
assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
- config.allowReminders = true;
+ if (Flags.modesUi()) {
+ config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy)
+ .allowReminders(true)
+ .build();
+ } else {
+ config.setAllowReminders(true);
+ }
assertFalse(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
- config.allowReminders = false;
+ if (Flags.modesUi()) {
+ config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy)
+ .allowReminders(false)
+ .build();
+ } else {
+ config.setAllowReminders(false);
+ }
+ assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
config.areChannelsBypassingDnd = true;
+ assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
+
+ if (Flags.modesUi()) {
+ config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy)
+ .allowPriorityChannels(true)
+ .build();
+ } else {
+ config.setAllowPriorityChannels(true);
+ }
+
assertFalse(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
+
config.areChannelsBypassingDnd = false;
+ if (Flags.modesUi()) {
+ config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy)
+ .allowPriorityChannels(false)
+ .build();
+ } else {
+ config.setAllowPriorityChannels(false);
+ }
assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
}
@@ -122,6 +178,8 @@
@Test
public void testZenPolicyToNotificationPolicy_classic() {
ZenModeConfig config = getMutedAllConfig();
+ // this shouldn't usually be directly set, but since it's a test that involved the default
+ // policy, calling setNotificationPolicy as a precondition may obscure issues
config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE;
// Explicitly allow conversations from priority senders to make sure that goes through
@@ -154,8 +212,9 @@
@Test
public void testZenPolicyToNotificationPolicy() {
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
ZenModeConfig config = getMutedAllConfig();
+ // this shouldn't usually be directly set, but since it's a test that involved the default
+ // policy, calling setNotificationPolicy as a precondition may obscure issues
config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE;
// Explicitly allow conversations from priority senders to make sure that goes through
@@ -194,14 +253,16 @@
@Test
public void testZenPolicyToNotificationPolicy_unsetChannelsTakesDefault() {
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
ZenModeConfig config = new ZenModeConfig();
ZenPolicy zenPolicy = new ZenPolicy.Builder().build();
// When allowChannels is not set to anything in the ZenPolicy builder, make sure it takes
// the default value from the zen mode config.
Policy policy = config.toNotificationPolicy(zenPolicy);
- assertEquals(config.allowPriorityChannels, policy.allowPriorityChannels());
+ assertEquals(Flags.modesUi()
+ ? config.manualRule.zenPolicy.getPriorityChannelsAllowed() == STATE_ALLOW
+ : config.isAllowPriorityChannels(),
+ policy.allowPriorityChannels());
}
@Test
@@ -219,18 +280,22 @@
.build();
ZenModeConfig config = getMutedAllConfig();
- config.allowAlarms = true;
- config.allowReminders = true;
- config.allowEvents = true;
- config.allowCalls = true;
- config.allowCallsFrom = Policy.PRIORITY_SENDERS_CONTACTS;
- config.allowMessages = true;
- config.allowMessagesFrom = Policy.PRIORITY_SENDERS_STARRED;
- config.allowConversations = false;
- config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE;
- config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS;
- config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_AMBIENT;
- ZenPolicy actual = config.toZenPolicy();
+ if (Flags.modesUi()) {
+ config.manualRule.zenPolicy = expected.copy();
+ } else {
+ config.setAllowAlarms(true);
+ config.setAllowReminders(true);
+ config.setAllowEvents(true);
+ config.setAllowCalls(true);
+ config.setAllowCallsFrom(Policy.PRIORITY_SENDERS_CONTACTS);
+ config.setAllowMessages(true);
+ config.setAllowMessagesFrom(Policy.PRIORITY_SENDERS_STARRED);
+ config.setAllowConversationsFrom(CONVERSATION_SENDERS_NONE);
+ config.setSuppressedVisualEffects(config.getSuppressedVisualEffects()
+ | Policy.SUPPRESSED_EFFECT_BADGE | Policy.SUPPRESSED_EFFECT_LIGHTS
+ | Policy.SUPPRESSED_EFFECT_AMBIENT);
+ }
+ ZenPolicy actual = config.getZenPolicy();
assertEquals(expected.getVisualEffectBadge(), actual.getVisualEffectBadge());
assertEquals(expected.getPriorityCategoryAlarms(), actual.getPriorityCategoryAlarms());
@@ -247,7 +312,6 @@
@Test
public void testZenConfigToZenPolicy() {
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
ZenPolicy expected = new ZenPolicy.Builder()
.allowAlarms(true)
.allowReminders(true)
@@ -262,19 +326,24 @@
.build();
ZenModeConfig config = getMutedAllConfig();
- config.allowAlarms = true;
- config.allowReminders = true;
- config.allowEvents = true;
- config.allowCalls = true;
- config.allowCallsFrom = Policy.PRIORITY_SENDERS_CONTACTS;
- config.allowMessages = true;
- config.allowMessagesFrom = Policy.PRIORITY_SENDERS_STARRED;
- config.allowConversations = false;
- config.allowPriorityChannels = false;
- config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE;
- config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS;
- config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_AMBIENT;
- ZenPolicy actual = config.toZenPolicy();
+ if (Flags.modesUi()) {
+ config.manualRule.zenPolicy = expected.copy();
+ } else {
+ config.setAllowAlarms(true);
+ config.setAllowReminders(true);
+
+ config.setAllowEvents(true);
+ config.setAllowCalls(true);
+ config.setAllowCallsFrom(Policy.PRIORITY_SENDERS_CONTACTS);
+ config.setAllowMessages(true);
+ config.setAllowMessagesFrom(Policy.PRIORITY_SENDERS_STARRED);
+ config.setAllowConversationsFrom(CONVERSATION_SENDERS_NONE);
+ config.setAllowPriorityChannels(false);
+ config.setSuppressedVisualEffects(config.getSuppressedVisualEffects()
+ | Policy.SUPPRESSED_EFFECT_BADGE | Policy.SUPPRESSED_EFFECT_LIGHTS
+ | Policy.SUPPRESSED_EFFECT_AMBIENT);
+ }
+ ZenPolicy actual = config.getZenPolicy();
assertEquals(expected.getVisualEffectBadge(), actual.getVisualEffectBadge());
assertEquals(expected.getPriorityCategoryAlarms(), actual.getPriorityCategoryAlarms());
@@ -296,20 +365,64 @@
assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
assertTrue(ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
- config.allowReminders = true;
+ if (Flags.modesUi()) {
+ config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy)
+ .allowReminders(true)
+ .build();
+ } else {
+ config.setAllowReminders(true);
+ }
assertFalse(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
assertFalse(ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
- config.allowReminders = false;
+
+ if (Flags.modesUi()) {
+ config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy)
+ .allowReminders(false)
+ .build();
+ } else {
+ config.setAllowReminders(false);
+ }
+
+ assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
+ assertTrue(ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
config.areChannelsBypassingDnd = true;
+ if (Flags.modesUi()) {
+ config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy)
+ .allowPriorityChannels(true)
+ .build();
+ } else {
+ config.setAllowPriorityChannels(true);
+ }
+
assertFalse(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
assertFalse(ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
- config.areChannelsBypassingDnd = false;
- config.allowAlarms = true;
+ config.areChannelsBypassingDnd = false;
+ if (Flags.modesUi()) {
+ config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy)
+ .allowPriorityChannels(false)
+ .build();
+ } else {
+ config.setAllowPriorityChannels(false);
+ }
+
+ if (Flags.modesUi()) {
+ config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy)
+ .allowAlarms(true)
+ .build();
+ } else {
+ config.setAllowAlarms(true);
+ }
assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
assertFalse(ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
- config.allowAlarms = false;
+ if (Flags.modesUi()) {
+ config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy)
+ .allowAlarms(false)
+ .build();
+ } else {
+ config.setAllowAlarms(false);
+ }
assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
assertTrue(ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
@@ -375,8 +488,6 @@
@Test
public void testWriteToParcel() {
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
-
ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
rule.configurationActivity = CONFIG_ACTIVITY;
rule.component = OWNER;
@@ -473,8 +584,6 @@
@Test
public void testRuleXml() throws Exception {
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
-
ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
rule.configurationActivity = CONFIG_ACTIVITY;
rule.component = OWNER;
@@ -719,8 +828,6 @@
@Test
public void testZenPolicyXml() throws Exception {
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
-
ZenPolicy policy = new ZenPolicy.Builder()
.allowCalls(ZenPolicy.PEOPLE_TYPE_CONTACTS)
.allowMessages(ZenPolicy.PEOPLE_TYPE_NONE)
@@ -770,63 +877,173 @@
fromXml.getVisualEffectNotificationList());
}
+ @Test
+ @EnableFlags(Flags.FLAG_MODES_UI)
+ public void testisManualActive_stateTrue() {
+ ZenModeConfig config = getMutedAllConfig();
+ final ZenModeConfig.ZenRule newRule = new ZenModeConfig.ZenRule();
+ newRule.type = AutomaticZenRule.TYPE_OTHER;
+ newRule.enabled = true;
+ newRule.conditionId = Uri.EMPTY;
+ newRule.allowManualInvocation = true;
+ config.manualRule = newRule;
+ config.manualRule.pkg = "android";
+ config.manualRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ config.manualRule.condition = new Condition(Uri.EMPTY, "", STATE_TRUE, SOURCE_USER_ACTION);
+
+ assertThat(config.isManualActive()).isTrue();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MODES_UI)
+ public void testisManualActive_stateFalse() {
+ ZenModeConfig config = getMutedAllConfig();
+ final ZenModeConfig.ZenRule newRule = new ZenModeConfig.ZenRule();
+ newRule.type = AutomaticZenRule.TYPE_OTHER;
+ newRule.enabled = true;
+ newRule.conditionId = Uri.EMPTY;
+ newRule.allowManualInvocation = true;
+ config.manualRule = newRule;
+ config.manualRule.pkg = "android";
+ config.manualRule.zenMode = ZEN_MODE_OFF;
+ config.manualRule.condition = new Condition(Uri.EMPTY, "", STATE_FALSE, SOURCE_USER_ACTION);
+
+ assertThat(config.isManualActive()).isFalse();
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MODES_UI)
+ public void testisManualActive_noState() {
+ ZenModeConfig config = getMutedAllConfig();
+ final ZenModeConfig.ZenRule newRule = new ZenModeConfig.ZenRule();
+ newRule.type = AutomaticZenRule.TYPE_OTHER;
+ newRule.enabled = true;
+ newRule.conditionId = Uri.EMPTY;
+ newRule.allowManualInvocation = true;
+ config.manualRule = newRule;
+ config.manualRule.pkg = "android";
+ config.manualRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+
+ assertThat(config.isManualActive()).isTrue();
+ }
+
+ @Test
+ public void testisManualActive_noRule() {
+ ZenModeConfig config = getMutedAllConfig();
+
+ assertThat(config.isManualActive()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MODES_UI)
+ public void testRuleXml_manual_upgrade() throws Exception {
+ ZenModeConfig config = getMutedAllConfig();
+ final ZenModeConfig.ZenRule newRule = new ZenModeConfig.ZenRule();
+ newRule.type = AutomaticZenRule.TYPE_OTHER;
+ newRule.enabled = true;
+ newRule.conditionId = Uri.EMPTY;
+ newRule.allowManualInvocation = true;
+ newRule.pkg = "android";
+ newRule.zenMode = ZEN_MODE_OFF;
+ config.manualRule = newRule;
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ writeRuleXml(newRule, baos);
+ ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+ ZenModeConfig.ZenRule fromXml = readRuleXml(bais);
+
+ assertThat(fromXml.zenPolicy).isEqualTo(config.getZenPolicy());
+ }
+
private ZenModeConfig getMutedRingerConfig() {
ZenModeConfig config = new ZenModeConfig();
- // Allow alarms, media
- config.allowAlarms = true;
- config.allowMedia = true;
- // All sounds that respect the ringer are not allowed
- config.allowSystem = false;
- config.allowCalls = false;
- config.allowRepeatCallers = false;
- config.allowMessages = false;
- config.allowReminders = false;
- config.allowEvents = false;
+ if (Flags.modesUi()) {
+ config.manualRule.zenPolicy = new ZenPolicy.Builder()
+ .disallowAllSounds()
+ .allowAlarms(true)
+ .allowMedia(true)
+ .allowPriorityChannels(false)
+ .showAllVisualEffects()
+ .build();
+ } else {
+ // Allow alarms, media
+ config.setAllowAlarms(true);
+ config.setAllowMedia(true);
+
+ // All sounds that respect the ringer are not allowed
+ config.setAllowSystem(false);
+ config.setAllowCalls(false);
+ config.setAllowRepeatCallers(false);
+ config.setAllowMessages(false);
+ config.setAllowReminders(false);
+ config.setAllowEvents(false);
+ config.setSuppressedVisualEffects(0);
+ config.setAllowPriorityChannels(false);
+ }
config.areChannelsBypassingDnd = false;
- config.suppressedVisualEffects = 0;
-
return config;
}
private ZenModeConfig getCustomConfig() {
ZenModeConfig config = new ZenModeConfig();
- // Some sounds allowed
- config.allowAlarms = true;
- config.allowMedia = false;
- config.allowSystem = false;
- config.allowCalls = true;
- config.allowRepeatCallers = true;
- config.allowMessages = false;
- config.allowReminders = false;
- config.allowEvents = false;
- config.areChannelsBypassingDnd = false;
- config.allowCallsFrom = ZenModeConfig.SOURCE_ANYONE;
- config.allowMessagesFrom = ZenModeConfig.SOURCE_ANYONE;
- config.allowConversations = true;
- config.allowConversationsFrom = CONVERSATION_SENDERS_IMPORTANT;
- config.suppressedVisualEffects = 0;
+ if (Flags.modesUi()) {
+ config.manualRule.zenPolicy = new ZenPolicy.Builder()
+ .disallowAllSounds()
+ .allowAlarms(true)
+ .allowCalls(PEOPLE_TYPE_ANYONE)
+ .allowRepeatCallers(true)
+ .allowConversations(CONVERSATION_SENDERS_IMPORTANT)
+ .allowPriorityChannels(true)
+ .showAllVisualEffects()
+ .build();
+ } else {
+ // Some sounds allowed
+ config.setAllowAlarms(true);
+ config.setAllowMedia(false);
+ config.setAllowSystem(false);
+ config.setAllowCalls(true);
+ config.setAllowRepeatCallers(true);
+ config.setAllowMessages(false);
+ config.setAllowReminders(false);
+ config.setAllowEvents(false);
+ config.setAllowCallsFrom(ZenModeConfig.SOURCE_ANYONE);
+ config.setAllowMessagesFrom(ZenModeConfig.SOURCE_ANYONE);
+ config.setAllowConversations(true);
+ config.setAllowConversationsFrom(CONVERSATION_SENDERS_IMPORTANT);
+ config.setSuppressedVisualEffects(0);
+ config.setAllowPriorityChannels(true);
+ }
+ config.areChannelsBypassingDnd = false;
return config;
}
private ZenModeConfig getMutedAllConfig() {
ZenModeConfig config = new ZenModeConfig();
- // No sounds allowed
- config.allowAlarms = false;
- config.allowMedia = false;
- config.allowSystem = false;
- config.allowCalls = false;
- config.allowRepeatCallers = false;
- config.allowMessages = false;
- config.allowReminders = false;
- config.allowEvents = false;
- config.areChannelsBypassingDnd = false;
- config.allowConversations = false;
- config.allowConversationsFrom = ZenPolicy.CONVERSATION_SENDERS_NONE;
- config.suppressedVisualEffects = 0;
+ if (Flags.modesUi()) {
+ config.manualRule.zenPolicy = new ZenPolicy.Builder()
+ .disallowAllSounds()
+ .showAllVisualEffects()
+ .allowPriorityChannels(false)
+ .build();
+ } else {
+ // No sounds allowed
+ config.setAllowAlarms(false);
+ config.setAllowMedia(false);
+ config.setAllowSystem(false);
+ config.setAllowCalls(false);
+ config.setAllowRepeatCallers(false);
+ config.setAllowMessages(false);
+ config.setAllowReminders(false);
+ config.setAllowEvents(false);
+ config.setAllowConversations(false);
+ config.setAllowConversationsFrom(CONVERSATION_SENDERS_NONE);
+ config.setSuppressedVisualEffects(0);
+ }
+ config.areChannelsBypassingDnd = false;
return config;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
index 2e64645..26a13cb 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
@@ -16,6 +16,8 @@
package com.android.server.notification;
+import static android.app.Flags.FLAG_MODES_UI;
+
import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.assertEquals;
@@ -27,8 +29,10 @@
import android.app.AutomaticZenRule;
import android.app.Flags;
+import android.app.NotificationManager;
import android.content.ComponentName;
import android.net.Uri;
+import android.platform.test.flag.junit.FlagsParameterization;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.service.notification.Condition;
@@ -37,7 +41,6 @@
import android.service.notification.ZenModeDiff;
import android.service.notification.ZenModeDiff.RuleDiff;
import android.service.notification.ZenPolicy;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.ArrayMap;
@@ -60,8 +63,11 @@
import java.util.Optional;
import java.util.Set;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(ParameterizedAndroidJunit4.class)
@TestableLooper.RunWithLooper
public class ZenModeDiffTest extends UiServiceTestCase {
// Base set of exempt fields independent of fields that are enabled/disabled via flags.
@@ -91,6 +97,16 @@
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return FlagsParameterization.allCombinationsOf(
+ FLAG_MODES_UI);
+ }
+
+ public ZenModeDiffTest(FlagsParameterization flags) {
+ mSetFlagsRule.setFlagsParameterization(flags);
+ }
+
@Test
public void testRuleDiff_addRemoveSame() {
// Test add, remove, and both sides same
@@ -220,21 +236,35 @@
ZenModeConfig c2 = new ZenModeConfig();
// set c1 and c2 to have some different senders
- c1.allowMessagesFrom = ZenModeConfig.SOURCE_STAR;
- c2.allowMessagesFrom = ZenModeConfig.SOURCE_CONTACT;
- c1.allowConversationsFrom = ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
- c2.allowConversationsFrom = ZenPolicy.CONVERSATION_SENDERS_NONE;
+ NotificationManager.Policy c1Policy = c1.toNotificationPolicy();
+ c1.applyNotificationPolicy(new NotificationManager.Policy(
+ c1Policy.priorityCategories, c1Policy.priorityCallSenders,
+ c1Policy.PRIORITY_SENDERS_STARRED, c1Policy.suppressedVisualEffects,
+ c1Policy.state, ZenPolicy.CONVERSATION_SENDERS_IMPORTANT));
+
+ NotificationManager.Policy c2Policy = c1.toNotificationPolicy();
+ c2.applyNotificationPolicy(new NotificationManager.Policy(
+ c2Policy.priorityCategories, c2Policy.priorityCallSenders,
+ c2Policy.PRIORITY_SENDERS_CONTACTS, c2Policy.suppressedVisualEffects,
+ c2Policy.state, ZenPolicy.CONVERSATION_SENDERS_NONE));
ZenModeDiff.ConfigDiff d = new ZenModeDiff.ConfigDiff(c1, c2);
assertTrue(d.hasDiff());
- // Diff in top-level fields
- assertTrue(d.getDiffForField("allowMessagesFrom").hasDiff());
- assertTrue(d.getDiffForField("allowConversationsFrom").hasDiff());
+ if (!Flags.modesUi()) {
+ // Diff in top-level fields
+ assertTrue(d.getDiffForField("allowMessagesFrom").hasDiff());
+ assertTrue(d.getDiffForField("allowConversationsFrom").hasDiff());
- // Bonus testing of stringification of people senders and conversation senders
- assertTrue(d.toString().contains("allowMessagesFrom:stars->contacts"));
- assertTrue(d.toString().contains("allowConversationsFrom:important->none"));
+ // Bonus testing of stringification of people senders and conversation senders
+ assertTrue(d.toString().contains("allowMessagesFrom:stars->contacts"));
+ assertTrue(d.toString().contains("allowConversationsFrom:important->none"));
+ } else {
+ RuleDiff r = d.getManualRuleDiff();
+ assertNotNull(r);
+ ZenModeDiff.FieldDiff p = r.getDiffForField(RuleDiff.FIELD_ZEN_POLICY);
+ assertNotNull(p);
+ }
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 72ace84..201b286 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -18,6 +18,8 @@
import static android.app.AutomaticZenRule.TYPE_BEDTIME;
import static android.app.AutomaticZenRule.TYPE_IMMERSIVE;
+import static android.app.Flags.FLAG_MODES_API;
+import static android.app.Flags.FLAG_MODES_UI;
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ACTIVATED;
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_DEACTIVATED;
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_DISABLED;
@@ -41,11 +43,9 @@
import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS;
import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_STARRED;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
+import static android.app.NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND;
+import static android.app.NotificationManager.Policy.STATE_PRIORITY_CHANNELS_BLOCKED;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
@@ -64,6 +64,11 @@
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_CONTACTS;
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_NONE;
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_STARRED;
+import static android.service.notification.ZenPolicy.VISUAL_EFFECT_AMBIENT;
+import static android.service.notification.ZenPolicy.VISUAL_EFFECT_BADGE;
+import static android.service.notification.ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT;
+import static android.service.notification.ZenPolicy.VISUAL_EFFECT_LIGHTS;
+import static android.service.notification.ZenPolicy.VISUAL_EFFECT_PEEK;
import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.LOG_DND_STATE_EVENTS;
import static com.android.os.dnd.DNDProtoEnums.PEOPLE_STARRED;
@@ -75,6 +80,7 @@
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
@@ -131,6 +137,7 @@
import android.os.UserHandle;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.FlagsParameterization;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.provider.Settings.Global;
@@ -140,7 +147,6 @@
import android.service.notification.ZenAdapters;
import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenModeConfig;
-import android.service.notification.ZenModeConfig.ConfigChangeOrigin;
import android.service.notification.ZenModeConfig.ScheduleInfo;
import android.service.notification.ZenModeConfig.ZenRule;
import android.service.notification.ZenModeDiff;
@@ -172,8 +178,6 @@
import com.google.common.truth.Correspondence;
import com.google.common.util.concurrent.SettableFuture;
import com.google.protobuf.InvalidProtocolBufferException;
-import com.google.testing.junit.testparameterinjector.TestParameter;
-import com.google.testing.junit.testparameterinjector.TestParameterInjector;
import org.junit.Before;
import org.junit.Rule;
@@ -202,9 +206,12 @@
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
@SmallTest
@SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service.
-@RunWith(TestParameterInjector.class)
+@RunWith(ParameterizedAndroidJunit4.class)
@TestableLooper.RunWithLooper
public class ZenModeHelperTest extends UiServiceTestCase {
@@ -268,6 +275,16 @@
ZenModeEventLoggerFake mZenModeEventLogger;
private String mPkg;
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return FlagsParameterization.progressionOf(FLAG_MODES_API,
+ FLAG_MODES_UI);
+ }
+
+ public ZenModeHelperTest(FlagsParameterization flags) {
+ mSetFlagsRule.setFlagsParameterization(flags);
+ }
+
@Before
public void setUp() throws PackageManager.NameNotFoundException {
MockitoAnnotations.initMocks(this);
@@ -677,7 +694,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void testTotalSilence_consolidatedPolicyDisallowsAll() {
// Start with zen mode off just to make sure global/manual mode isn't doing anything.
mZenModeHelper.mZenMode = ZEN_MODE_OFF;
@@ -711,7 +728,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void testAlarmsOnly_consolidatedPolicyOnlyAllowsAlarmsAndMedia() {
// Start with zen mode off just to make sure global/manual mode isn't doing anything.
mZenModeHelper.mZenMode = ZEN_MODE_OFF;
@@ -802,15 +819,11 @@
// 1. Current ringer is normal
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
// Set zen to priority-only with all notification sounds muted (so ringer will be muted)
+ Policy totalSilence = new Policy(0,0,0);
+ mZenModeHelper.setNotificationPolicy(totalSilence, UPDATE_ORIGIN_APP, 1);
mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
- mZenModeHelper.mConfig.allowReminders = false;
- mZenModeHelper.mConfig.allowCalls = false;
- mZenModeHelper.mConfig.allowMessages = false;
- mZenModeHelper.mConfig.allowEvents = false;
- mZenModeHelper.mConfig.allowRepeatCallers = false;
- mZenModeHelper.mConfig.allowConversations = false;
- // 2. apply priority only zen - verify ringer is unchanged
+ // 2. verify ringer is unchanged
mZenModeHelper.applyZenToRingerMode();
verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_SILENT,
mZenModeHelper.TAG);
@@ -844,9 +857,8 @@
public void testRingerAffectedStreamsPriorityOnly() {
// in priority only mode:
// ringtone, notification and system streams are affected by ringer mode
- mZenModeHelper.mConfig.allowAlarms = true;
- mZenModeHelper.mConfig.allowReminders = true;
- mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, Uri.EMPTY,
+ UPDATE_ORIGIN_APP, "test", "caller", 1);
ZenModeHelper.RingerModeDelegate ringerModeDelegateRingerMuted =
mZenModeHelper.new RingerModeDelegate();
@@ -862,13 +874,9 @@
// even when ringer is muted (since all ringer sounds cannot bypass DND),
// system stream is still affected by ringer mode
- mZenModeHelper.mConfig.allowSystem = false;
- mZenModeHelper.mConfig.allowReminders = false;
- mZenModeHelper.mConfig.allowCalls = false;
- mZenModeHelper.mConfig.allowMessages = false;
- mZenModeHelper.mConfig.allowEvents = false;
- mZenModeHelper.mConfig.allowRepeatCallers = false;
- mZenModeHelper.mConfig.allowConversations = false;
+ mZenModeHelper.setNotificationPolicy(new Policy(0,0,0), UPDATE_ORIGIN_APP, 1);
+ mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, Uri.EMPTY,
+ UPDATE_ORIGIN_APP, "test", "caller", 1);
ZenModeHelper.RingerModeDelegate ringerModeDelegateRingerNotMuted =
mZenModeHelper.new RingerModeDelegate();
@@ -885,7 +893,7 @@
}
@Test
- public void testZenSetInternalRinger_NotAllPriorityNotificationSoundsMuted_StartNormal() {
+ public void applyZenToRingerMode_ZEN_MODE_IMPORTANT_INTERRUPTIONS() {
AudioManagerInternal mAudioManager = mock(AudioManagerInternal.class);
mZenModeHelper.mAudioManager = mAudioManager;
Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_RINGER_LEVEL,
@@ -894,7 +902,6 @@
// 1. Current ringer is normal
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
- mZenModeHelper.mConfig.allowReminders = true;
// 2. apply priority only zen - verify ringer is normal
mZenModeHelper.applyZenToRingerMode();
@@ -919,7 +926,6 @@
// 1. Current ringer is silent
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
- mZenModeHelper.mConfig.allowReminders = true;
// 2. apply priority only zen - verify ringer is silent
mZenModeHelper.applyZenToRingerMode();
@@ -945,7 +951,6 @@
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
// Set zen to priority-only with all notification sounds muted (so ringer will be muted)
mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
- mZenModeHelper.mConfig.allowReminders = true;
// 2. apply priority only zen - verify zen will still be normal
mZenModeHelper.applyZenToRingerMode();
@@ -977,11 +982,9 @@
// apply zen off multiple times - verify ringer is not set to normal
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
- mZenModeHelper.mZenMode = ZEN_MODE_OFF;
- mZenModeHelper.mConfig = null; // will evaluate config to zen mode off
for (int i = 0; i < 3; i++) {
- // if zen doesn't change, zen should not reapply itself to the ringer
- mZenModeHelper.evaluateZenModeLocked(UPDATE_ORIGIN_UNKNOWN, "test", true);
+ mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, Uri.EMPTY,
+ UPDATE_ORIGIN_APP, "test", "caller", 1);
}
verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
mZenModeHelper.TAG);
@@ -991,8 +994,6 @@
public void testSilentRingerSavedOnZenOff_startsZenOn() {
AudioManagerInternal mAudioManager = mock(AudioManagerInternal.class);
mZenModeHelper.mAudioManager = mAudioManager;
- mZenModeHelper.mZenMode = ZEN_MODE_OFF;
- mZenModeHelper.mConfig = new ZenModeConfig();
// previously set silent ringer
ZenModeHelper.RingerModeDelegate ringerModeDelegate =
@@ -1003,12 +1004,12 @@
assertEquals(AudioManager.RINGER_MODE_SILENT, Global.getInt(mContext.getContentResolver(),
Global.ZEN_MODE_RINGER_LEVEL, AudioManager.RINGER_MODE_NORMAL));
- // apply zen off multiple times - verify ringer is not set to normal
+ // apply zen on multiple times - verify ringer is not set to normal
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
- mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
for (int i = 0; i < 3; i++) {
// if zen doesn't change, zen should not reapply itself to the ringer
- mZenModeHelper.evaluateZenModeLocked(UPDATE_ORIGIN_UNKNOWN, "test", true);
+ mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, Uri.EMPTY,
+ UPDATE_ORIGIN_APP, "test", "caller", 1);
}
verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
mZenModeHelper.TAG);
@@ -1018,8 +1019,6 @@
public void testVibrateRingerSavedOnZenOff_startsZenOn() {
AudioManagerInternal mAudioManager = mock(AudioManagerInternal.class);
mZenModeHelper.mAudioManager = mAudioManager;
- mZenModeHelper.mZenMode = ZEN_MODE_OFF;
- mZenModeHelper.mConfig = new ZenModeConfig();
// previously set silent ringer
ZenModeHelper.RingerModeDelegate ringerModeDelegate =
@@ -1032,10 +1031,10 @@
// apply zen off multiple times - verify ringer is not set to normal
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
- mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
for (int i = 0; i < 3; i++) {
// if zen doesn't change, zen should not reapply itself to the ringer
- mZenModeHelper.evaluateZenModeLocked(UPDATE_ORIGIN_UNKNOWN, "test", true);
+ mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, Uri.EMPTY,
+ UPDATE_ORIGIN_APP, "test", "caller", 1);
}
verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
mZenModeHelper.TAG);
@@ -1066,22 +1065,18 @@
@Test
public void testParcelConfig() {
- mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
- mZenModeHelper.mConfig.allowAlarms = false;
- mZenModeHelper.mConfig.allowMedia = false;
- mZenModeHelper.mConfig.allowSystem = false;
- mZenModeHelper.mConfig.allowReminders = true;
- mZenModeHelper.mConfig.allowCalls = true;
- mZenModeHelper.mConfig.allowMessages = true;
- mZenModeHelper.mConfig.allowEvents = true;
- mZenModeHelper.mConfig.allowRepeatCallers = true;
- mZenModeHelper.mConfig.allowConversations = true;
- mZenModeHelper.mConfig.allowConversationsFrom = ZenPolicy.CONVERSATION_SENDERS_ANYONE;
- mZenModeHelper.mConfig.suppressedVisualEffects = SUPPRESSED_EFFECT_BADGE;
- mZenModeHelper.mConfig.manualRule = new ZenModeConfig.ZenRule();
- mZenModeHelper.mConfig.manualRule.component = new ComponentName("a", "a");
- mZenModeHelper.mConfig.manualRule.enabled = true;
- mZenModeHelper.mConfig.manualRule.snoozing = true;
+ mZenModeHelper.setNotificationPolicy(new Policy(PRIORITY_CATEGORY_EVENTS
+ | PRIORITY_CATEGORY_MESSAGES | PRIORITY_CATEGORY_REPEAT_CALLERS
+ | PRIORITY_CATEGORY_CONVERSATIONS, PRIORITY_SENDERS_STARRED,
+ PRIORITY_SENDERS_STARRED, 0, CONVERSATION_SENDERS_ANYONE), UPDATE_ORIGIN_UNKNOWN,
+ 1);
+ mZenModeHelper.setManualZenRuleDeviceEffects(new ZenDeviceEffects.Builder()
+ .setShouldDimWallpaper(true)
+ .setShouldDisplayGrayscale(true)
+ .setShouldUseNightMode(true)
+ .build(), UPDATE_ORIGIN_UNKNOWN, "test", 1);
+ mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, Uri.EMPTY,
+ UPDATE_ORIGIN_UNKNOWN, "test", "me", 1);
ZenModeConfig actual = mZenModeHelper.mConfig.copy();
@@ -1090,24 +1085,17 @@
@Test
public void testWriteXml() throws Exception {
- mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
- mZenModeHelper.mConfig.allowAlarms = false;
- mZenModeHelper.mConfig.allowMedia = false;
- mZenModeHelper.mConfig.allowSystem = false;
- mZenModeHelper.mConfig.allowReminders = true;
- mZenModeHelper.mConfig.allowCalls = true;
- mZenModeHelper.mConfig.allowMessages = true;
- mZenModeHelper.mConfig.allowEvents = true;
- mZenModeHelper.mConfig.allowRepeatCallers = true;
- mZenModeHelper.mConfig.allowConversations = true;
- mZenModeHelper.mConfig.allowConversationsFrom = ZenPolicy.CONVERSATION_SENDERS_ANYONE;
- mZenModeHelper.mConfig.suppressedVisualEffects = SUPPRESSED_EFFECT_BADGE;
- mZenModeHelper.mConfig.manualRule = new ZenModeConfig.ZenRule();
- mZenModeHelper.mConfig.manualRule.zenMode =
- ZEN_MODE_IMPORTANT_INTERRUPTIONS;
- mZenModeHelper.mConfig.manualRule.component = new ComponentName("a", "a");
- mZenModeHelper.mConfig.manualRule.pkg = "a";
- mZenModeHelper.mConfig.manualRule.enabled = true;
+ mZenModeHelper.setNotificationPolicy(new Policy(PRIORITY_CATEGORY_EVENTS
+ | PRIORITY_CATEGORY_MESSAGES | PRIORITY_CATEGORY_REPEAT_CALLERS
+ | PRIORITY_CATEGORY_CONVERSATIONS, PRIORITY_SENDERS_STARRED,
+ PRIORITY_SENDERS_STARRED, SUPPRESSED_EFFECT_BADGE, CONVERSATION_SENDERS_ANYONE),
+ UPDATE_ORIGIN_UNKNOWN, 1);
+ mZenModeHelper.setManualZenRuleDeviceEffects(new ZenDeviceEffects.Builder()
+ .setShouldDimWallpaper(true)
+ .setShouldDisplayGrayscale(true)
+ .build(), UPDATE_ORIGIN_UNKNOWN, "test", 1);
+ mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, Uri.EMPTY,
+ UPDATE_ORIGIN_UNKNOWN, "test", "me", 1);
ZenModeConfig expected = mZenModeHelper.mConfig.copy();
if (Flags.modesUi()) {
@@ -1127,10 +1115,10 @@
@Test
public void testProto() throws InvalidProtocolBufferException {
- mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
- // existence of manual rule means it should be in output
- mZenModeHelper.mConfig.manualRule = new ZenModeConfig.ZenRule();
- mZenModeHelper.mConfig.manualRule.pkg = "android"; // system
+ mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
+ Flags.modesApi() ? UPDATE_ORIGIN_USER : UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI,
+ null, "test", CUSTOM_PKG_UID);
+
mZenModeHelper.mConfig.automaticRules = new ArrayMap<>(); // no automatic rules
List<String> ids = new ArrayList<>();
@@ -1151,7 +1139,6 @@
assertTrue(cfg.getEnabled());
assertFalse(cfg.getChannelsBypassing());
}
- assertEquals(Process.SYSTEM_UID, cfg.getUid());
String name = cfg.getId();
assertTrue("unexpected rule id", ids.contains(name));
ids.remove(name);
@@ -1255,10 +1242,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void testProtoWithAutoRuleCustomPolicy() throws Exception {
- // allowChannels is only valid under modes_api.
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
-
setupZenConfig();
// clear any automatic rules just to make sure
mZenModeHelper.mConfig.automaticRules = new ArrayMap<>();
@@ -1299,7 +1284,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void testProtoWithAutoRuleWithModifiedFields() throws Exception {
setupZenConfig();
mZenModeHelper.mConfig.automaticRules = new ArrayMap<>();
@@ -1384,8 +1369,8 @@
public void testProtoWithManualRule() throws Exception {
setupZenConfig();
mZenModeHelper.mConfig.automaticRules = getCustomAutomaticRules();
- mZenModeHelper.mConfig.manualRule = new ZenModeConfig.ZenRule();
- mZenModeHelper.mConfig.manualRule.enabled = true;
+ mZenModeHelper.setManualZenMode(INTERRUPTION_FILTER_PRIORITY, Uri.EMPTY, UPDATE_ORIGIN_APP,
+ "test", "me", 1);
List<StatsEvent> events = new LinkedList<>();
mZenModeHelper.pullRules(events);
@@ -1408,15 +1393,15 @@
// Setup configs for user 10 and 11.
setupZenConfig();
ZenModeConfig config10 = mZenModeHelper.mConfig.copy();
+ Policy policy = new Policy(PRIORITY_CATEGORY_MEDIA | PRIORITY_CATEGORY_ALARMS, 0, 0);
+ config10.applyNotificationPolicy(policy);
config10.user = 10;
- config10.allowAlarms = true;
- config10.allowMedia = true;
mZenModeHelper.setConfig(config10, null, UPDATE_ORIGIN_INIT, "writeXml",
Process.SYSTEM_UID);
ZenModeConfig config11 = mZenModeHelper.mConfig.copy();
config11.user = 11;
- config11.allowAlarms = false;
- config11.allowMedia = false;
+ policy = new Policy(0, 0, 0);
+ config11.applyNotificationPolicy(policy);
mZenModeHelper.setConfig(config11, null, UPDATE_ORIGIN_INIT, "writeXml",
Process.SYSTEM_UID);
@@ -1583,8 +1568,9 @@
}
@Test
- public void testReadXmlRulesNotOverriden() throws Exception {
+ public void testReadXmlRulesNotOverridden() throws Exception {
setupZenConfig();
+ Policy originalPolicy = mZenModeHelper.getNotificationPolicy();
// automatic zen rule is enabled on upgrade so rules should not be overriden to default
ArrayMap<String, ZenModeConfig.ZenRule> enabledAutoRule = new ArrayMap<>();
@@ -1607,7 +1593,7 @@
mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
assertTrue(mZenModeHelper.mConfig.automaticRules.containsKey("customRule"));
- setupZenConfigMaintained();
+ assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy());
}
@Test
@@ -1626,7 +1612,7 @@
parser.nextTag();
mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
- assertEquals(0, mZenModeHelper.mConfig.suppressedVisualEffects);
+ assertTrue(mZenModeHelper.mConfig.getZenPolicy().shouldShowAllVisualEffects());
xml = "<zen version=\"6\" user=\"0\">\n"
+ "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" "
@@ -1642,7 +1628,7 @@
parser.nextTag();
mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
- assertEquals(0, mZenModeHelper.mConfig.suppressedVisualEffects);
+ assertTrue(mZenModeHelper.mConfig.getZenPolicy().shouldShowAllVisualEffects());
}
@Test
@@ -1661,7 +1647,7 @@
parser.nextTag();
mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
- assertEquals(0, mZenModeHelper.mConfig.suppressedVisualEffects);
+ assertTrue(mZenModeHelper.mConfig.getZenPolicy().shouldShowAllVisualEffects());
}
@Test
@@ -1680,11 +1666,16 @@
parser.nextTag();
mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
- assertEquals(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
- | SUPPRESSED_EFFECT_LIGHTS
- | SUPPRESSED_EFFECT_AMBIENT
- | SUPPRESSED_EFFECT_PEEK,
- mZenModeHelper.mConfig.suppressedVisualEffects);
+ assertThat(mZenModeHelper.mConfig.getZenPolicy()
+ .isVisualEffectAllowed(VISUAL_EFFECT_FULL_SCREEN_INTENT, true)).isFalse();
+ assertThat(mZenModeHelper.mConfig.getZenPolicy()
+ .isVisualEffectAllowed(VISUAL_EFFECT_LIGHTS, true)).isFalse();
+ assertThat(mZenModeHelper.mConfig.getZenPolicy()
+ .isVisualEffectAllowed(VISUAL_EFFECT_PEEK, true)).isFalse();
+ assertThat(mZenModeHelper.mConfig.getZenPolicy()
+ .isVisualEffectAllowed(VISUAL_EFFECT_AMBIENT, true)).isFalse();
+ assertThat(mZenModeHelper.mConfig.getZenPolicy()
+ .isVisualEffectAllowed(VISUAL_EFFECT_BADGE, true)).isTrue();
xml = "<zen version=\"6\" user=\"0\">\n"
+ "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" "
@@ -1700,7 +1691,10 @@
parser.nextTag();
mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
- assertEquals(SUPPRESSED_EFFECT_PEEK, mZenModeHelper.mConfig.suppressedVisualEffects);
+ assertThat(mZenModeHelper.mConfig.getZenPolicy()
+ .isVisualEffectAllowed(VISUAL_EFFECT_PEEK, true)).isFalse();
+ assertThat(mZenModeHelper.mConfig.getZenPolicy()
+ .isVisualEffectAllowed(VISUAL_EFFECT_AMBIENT, true)).isTrue();
xml = "<zen version=\"6\" user=\"0\">\n"
+ "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" "
@@ -1716,18 +1710,23 @@
parser.nextTag();
mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
- assertEquals(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
- | SUPPRESSED_EFFECT_LIGHTS
- | SUPPRESSED_EFFECT_AMBIENT,
- mZenModeHelper.mConfig.suppressedVisualEffects);
+ assertThat(mZenModeHelper.mConfig.getZenPolicy()
+ .isVisualEffectAllowed(VISUAL_EFFECT_FULL_SCREEN_INTENT, true)).isFalse();
+ assertThat(mZenModeHelper.mConfig.getZenPolicy()
+ .isVisualEffectAllowed(VISUAL_EFFECT_LIGHTS, true)).isFalse();
+ assertThat(mZenModeHelper.mConfig.getZenPolicy()
+ .isVisualEffectAllowed(VISUAL_EFFECT_AMBIENT, true)).isFalse();
+ assertThat(mZenModeHelper.mConfig.getZenPolicy()
+ .isVisualEffectAllowed(VISUAL_EFFECT_BADGE, true)).isTrue();
}
@Test
public void testReadXmlResetDefaultRules() throws Exception {
setupZenConfig();
+ Policy originalPolicy = mZenModeHelper.getNotificationPolicy();
// no enabled automatic zen rules and no default rules
- // so rules should be overriden by default rules
+ // so rules should be overridden by default rules
mZenModeHelper.mConfig.automaticRules = new ArrayMap<>();
// set previous version
@@ -1745,13 +1744,14 @@
assertTrue(rules.containsKey(defaultId));
}
- setupZenConfigMaintained();
+ assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy());
}
@Test
public void testReadXmlAllDisabledRulesResetDefaultRules() throws Exception {
setupZenConfig();
+ Policy originalPolicy = mZenModeHelper.getNotificationPolicy();
// all automatic zen rules are disabled on upgrade (and default rules don't already exist)
// so rules should be overriden by default rules
@@ -1782,12 +1782,13 @@
}
assertFalse(rules.containsKey("customRule"));
- setupZenConfigMaintained();
+ assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy());
}
@Test
public void testReadXmlOnlyOneDefaultRuleExists() throws Exception {
setupZenConfig();
+ Policy originalPolicy = mZenModeHelper.getNotificationPolicy();
// all automatic zen rules are disabled on upgrade and only one default rule exists
// so rules should be overriden to the default rules
@@ -1834,12 +1835,13 @@
}
assertFalse(rules.containsKey("customRule"));
- setupZenConfigMaintained();
+ assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy());
}
@Test
public void testReadXmlDefaultRulesExist() throws Exception {
setupZenConfig();
+ Policy originalPolicy = mZenModeHelper.getNotificationPolicy();
// Default rules exist so rules should not be overridden by defaults
ArrayMap<String, ZenModeConfig.ZenRule> automaticRules = new ArrayMap<>();
@@ -1897,13 +1899,13 @@
// check default rules
ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules;
- assertTrue(rules.size() != 0);
+ assertEquals(3, rules.size());
for (String defaultId : ZenModeConfig.DEFAULT_RULE_IDS) {
assertTrue(rules.containsKey(defaultId));
}
assertTrue(rules.containsKey("customRule"));
- setupZenConfigMaintained();
+ assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy());
List<StatsEvent> events = new LinkedList<>();
mZenModeHelper.pullRules(events);
@@ -1911,11 +1913,12 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void testReadXml_onModesApi_noUpgrade() throws Exception {
// When reading XML for something that is already on the modes API system, make sure no
// rules' policies get changed.
setupZenConfig();
+ Policy originalPolicy = mZenModeHelper.getNotificationPolicy();
// Shared for rules
ArrayMap<String, ZenModeConfig.ZenRule> enabledAutoRules = new ArrayMap<>();
@@ -1947,7 +1950,7 @@
mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
// basic check: global config maintained
- setupZenConfigMaintained();
+ assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy());
// Find our automatic rules.
ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules;
@@ -1958,12 +1961,13 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void testReadXml_upgradeToModesApi_makesCustomPolicies() throws Exception {
// When reading in an XML file written from a pre-modes-API version, confirm that we create
// a custom policy matching the global config for any automatic rule with no specified
// policy.
setupZenConfig();
+ Policy originalPolicy = mZenModeHelper.getNotificationPolicy();
ArrayMap<String, ZenModeConfig.ZenRule> enabledAutoRule = new ArrayMap<>();
ZenModeConfig.ZenRule customRule = new ZenModeConfig.ZenRule();
@@ -1985,7 +1989,7 @@
mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
// basic check: global config maintained
- setupZenConfigMaintained();
+ assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy());
// Find our automatic rule and check that it has a policy set now
ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules;
@@ -2009,12 +2013,13 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void testReadXml_upgradeToModesApi_fillsInCustomPolicies() throws Exception {
// When reading in an XML file written from a pre-modes-API version, confirm that for an
// underspecified ZenPolicy, we fill in all of the gaps with things from the global config
// in order to maintain consistency of behavior.
setupZenConfig();
+ Policy originalPolicy = mZenModeHelper.getNotificationPolicy();
ArrayMap<String, ZenModeConfig.ZenRule> enabledAutoRule = new ArrayMap<>();
ZenModeConfig.ZenRule customRule = new ZenModeConfig.ZenRule();
@@ -2041,7 +2046,7 @@
mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
// basic check: global config maintained
- setupZenConfigMaintained();
+ assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy());
// Find our automatic rule and check that it has a policy set now
ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules;
@@ -2068,7 +2073,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void testReadXml_upgradeToModesApi_existingDefaultRulesGetCustomPolicy()
throws Exception {
setupZenConfig();
@@ -2230,7 +2235,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void testDefaultRulesFromConfig_modesApi_getPolicies() {
// After mZenModeHelper was created, set some things in the policy so it's changed from
// default.
@@ -2389,7 +2394,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void testAddAutomaticZenRule_modesApi_fillsInDefaultValues() {
// When a new automatic zen rule is added with only some fields filled in, ensure that
// all unset fields are filled in with device defaults.
@@ -2436,19 +2441,19 @@
assertThat(rule2InConfig.zenPolicy.getPriorityMessageSenders())
.isEqualTo(PEOPLE_TYPE_CONTACTS);
assertThat(rule2InConfig.zenPolicy.getVisualEffectFullScreenIntent())
- .isEqualTo(ZenPolicy.STATE_ALLOW);
+ .isEqualTo(STATE_ALLOW);
// the rest of rule 2's settings should be the device defaults
assertThat(rule2InConfig.zenPolicy.getPriorityConversationSenders())
.isEqualTo(CONVERSATION_SENDERS_IMPORTANT);
assertThat(rule2InConfig.zenPolicy.getPriorityCategorySystem())
- .isEqualTo(ZenPolicy.STATE_DISALLOW);
+ .isEqualTo(STATE_DISALLOW);
assertThat(rule2InConfig.zenPolicy.getPriorityCategoryAlarms())
- .isEqualTo(ZenPolicy.STATE_ALLOW);
+ .isEqualTo(STATE_ALLOW);
assertThat(rule2InConfig.zenPolicy.getVisualEffectPeek())
- .isEqualTo(ZenPolicy.STATE_DISALLOW);
+ .isEqualTo(STATE_DISALLOW);
assertThat(rule2InConfig.zenPolicy.getVisualEffectNotificationList())
- .isEqualTo(ZenPolicy.STATE_ALLOW);
+ .isEqualTo(STATE_ALLOW);
}
@Test
@@ -2590,9 +2595,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void addAutomaticZenRule_fromApp_ignoresHiddenEffects() {
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
-
ZenDeviceEffects zde = new ZenDeviceEffects.Builder()
.setShouldDisplayGrayscale(true)
.setShouldSuppressAmbientDisplay(true)
@@ -2624,9 +2628,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void addAutomaticZenRule_fromSystem_respectsHiddenEffects() {
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
-
ZenDeviceEffects zde = new ZenDeviceEffects.Builder()
.setShouldDisplayGrayscale(true)
.setShouldSuppressAmbientDisplay(true)
@@ -2652,9 +2655,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void addAutomaticZenRule_fromUser_respectsHiddenEffects() throws Exception {
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
-
ZenDeviceEffects zde = new ZenDeviceEffects.Builder()
.setShouldDisplayGrayscale(true)
.setShouldSuppressAmbientDisplay(true)
@@ -2682,8 +2684,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void updateAutomaticZenRule_fromApp_preservesPreviousHiddenEffects() {
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
ZenDeviceEffects original = new ZenDeviceEffects.Builder()
.setShouldDisableTapToWake(true)
.addExtraEffect("extra")
@@ -2717,8 +2719,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void updateAutomaticZenRule_fromSystem_updatesHiddenEffects() {
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
ZenDeviceEffects original = new ZenDeviceEffects.Builder()
.setShouldDisableTapToWake(true)
.build();
@@ -2744,8 +2746,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void updateAutomaticZenRule_fromUser_updatesHiddenEffects() {
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
ZenDeviceEffects original = new ZenDeviceEffects.Builder()
.setShouldDisableTapToWake(true)
.build();
@@ -2775,7 +2777,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void updateAutomaticZenRule_nullPolicy_doesNothing() {
// Test that when updateAutomaticZenRule is called with a null policy, nothing changes
// about the existing policy.
@@ -2796,11 +2798,11 @@
AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
assertThat(savedRule.getZenPolicy().getPriorityCategoryCalls())
- .isEqualTo(ZenPolicy.STATE_DISALLOW);
+ .isEqualTo(STATE_DISALLOW);
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void updateAutomaticZenRule_overwritesExistingPolicy() {
// Test that when updating an automatic zen rule with an existing policy, the newly set
// fields overwrite those from the previous policy, but unset fields in the new policy
@@ -2826,18 +2828,18 @@
AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
assertThat(savedRule.getZenPolicy().getPriorityCategoryCalls())
- .isEqualTo(ZenPolicy.STATE_ALLOW); // from update
+ .isEqualTo(STATE_ALLOW); // from update
assertThat(savedRule.getZenPolicy().getPriorityCallSenders())
.isEqualTo(ZenPolicy.PEOPLE_TYPE_CONTACTS); // from update
assertThat(savedRule.getZenPolicy().getPriorityCategoryAlarms())
- .isEqualTo(ZenPolicy.STATE_DISALLOW); // from original
+ .isEqualTo(STATE_DISALLOW); // from original
assertThat(savedRule.getZenPolicy().getPriorityCategoryReminders())
- .isEqualTo(ZenPolicy.STATE_ALLOW); // from original
+ .isEqualTo(STATE_ALLOW); // from original
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void addAutomaticZenRule_withTypeBedtime_replacesDisabledSleeping() {
ZenRule sleepingRule = createCustomAutomaticRule(ZEN_MODE_IMPORTANT_INTERRUPTIONS,
ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID);
@@ -2857,7 +2859,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void addAutomaticZenRule_withTypeBedtime_keepsEnabledSleeping() {
ZenRule sleepingRule = createCustomAutomaticRule(ZEN_MODE_IMPORTANT_INTERRUPTIONS,
ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID);
@@ -2878,7 +2880,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void addAutomaticZenRule_withTypeBedtime_keepsCustomizedSleeping() {
ZenRule sleepingRule = createCustomAutomaticRule(ZEN_MODE_IMPORTANT_INTERRUPTIONS,
ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID);
@@ -2899,8 +2901,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void testSetManualZenMode() {
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
setupZenConfig();
// note that caller=null because that's how it comes in from NMS.setZenMode
@@ -2919,68 +2921,83 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
- @DisableFlags(Flags.FLAG_MODES_UI)
- public void setManualZenMode_off_snoozesActiveRules(@TestParameter ChangeOrigin setZenOrigin) {
- // Start with an active rule and an inactive rule.
- mZenModeHelper.mConfig.automaticRules.clear();
- AutomaticZenRule activeRule = new AutomaticZenRule.Builder("Test", CONDITION_ID)
- .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
- .build();
- String activeRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
- activeRule, UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID);
- mZenModeHelper.setAutomaticZenRuleState(activeRuleId, CONDITION_TRUE, UPDATE_ORIGIN_APP,
- CUSTOM_PKG_UID);
- AutomaticZenRule inactiveRule = new AutomaticZenRule.Builder("Test", CONDITION_ID)
- .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
- .build();
- String inactiveRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
- inactiveRule, UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID);
+ @EnableFlags(FLAG_MODES_API)
+ @DisableFlags(FLAG_MODES_UI)
+ public void setManualZenMode_off_snoozesActiveRules() {
+ for (ChangeOrigin origin : ChangeOrigin.values()) {
+ // Start with an active rule and an inactive rule.
+ mZenModeHelper.mConfig.automaticRules.clear();
+ AutomaticZenRule activeRule = new AutomaticZenRule.Builder("Test", CONDITION_ID)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .build();
+ String activeRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+ activeRule, UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID);
+ mZenModeHelper.setAutomaticZenRuleState(activeRuleId, CONDITION_TRUE, UPDATE_ORIGIN_APP,
+ CUSTOM_PKG_UID);
+ AutomaticZenRule inactiveRule = new AutomaticZenRule.Builder("Test", CONDITION_ID)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .build();
+ String inactiveRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+ inactiveRule, UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID);
- assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+ assertWithMessage("Failure for origin " + origin.name())
+ .that(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
- // User turns DND off.
- mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, setZenOrigin.value(),
- "snoozing", "systemui", Process.SYSTEM_UID);
- assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
- assertThat(mZenModeHelper.mConfig.automaticRules.get(activeRuleId).snoozing).isTrue();
- assertThat(mZenModeHelper.mConfig.automaticRules.get(inactiveRuleId).snoozing).isFalse();
+ // User turns DND off.
+ mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, origin.value(),
+ "snoozing", "systemui", Process.SYSTEM_UID);
+ assertWithMessage("Failure for origin " + origin.name())
+ .that(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
+ assertWithMessage("Failure for origin " + origin.name())
+ .that(mZenModeHelper.mConfig.automaticRules.get(activeRuleId).snoozing)
+ .isTrue();
+ assertWithMessage("Failure for origin " + origin.name())
+ .that(mZenModeHelper.mConfig.automaticRules.get(inactiveRuleId).snoozing)
+ .isFalse();
+ }
}
@Test
- @EnableFlags({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI})
- public void setManualZenMode_off_doesNotSnoozeRulesIfFromUser(
- @TestParameter ChangeOrigin setZenOrigin) {
- // Start with an active rule and an inactive rule
- mZenModeHelper.mConfig.automaticRules.clear();
- AutomaticZenRule activeRule = new AutomaticZenRule.Builder("Test", CONDITION_ID)
- .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
- .build();
- String activeRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
- activeRule, UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID);
- mZenModeHelper.setAutomaticZenRuleState(activeRuleId, CONDITION_TRUE, UPDATE_ORIGIN_APP,
- CUSTOM_PKG_UID);
- AutomaticZenRule inactiveRule = new AutomaticZenRule.Builder("Test", CONDITION_ID)
- .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
- .build();
- String inactiveRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
- inactiveRule, UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID);
+ @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ public void setManualZenMode_off_doesNotSnoozeRulesIfFromUser() {
+ for (ChangeOrigin origin : ChangeOrigin.values()) {
+ // Start with an active rule and an inactive rule
+ mZenModeHelper.mConfig.automaticRules.clear();
+ AutomaticZenRule activeRule = new AutomaticZenRule.Builder("Test", CONDITION_ID)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .build();
+ String activeRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+ activeRule, UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID);
+ mZenModeHelper.setAutomaticZenRuleState(activeRuleId, CONDITION_TRUE, UPDATE_ORIGIN_APP,
+ CUSTOM_PKG_UID);
+ AutomaticZenRule inactiveRule = new AutomaticZenRule.Builder("Test", CONDITION_ID)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .build();
+ String inactiveRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+ inactiveRule, UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID);
- assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
-
- // User turns DND off.
- mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, setZenOrigin.value(),
- "snoozing", "systemui", Process.SYSTEM_UID);
- ZenModeConfig config = mZenModeHelper.mConfig;
- if (setZenOrigin == ChangeOrigin.ORIGIN_USER) {
- // Other rule was unaffected.
assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
- assertThat(config.automaticRules.get(activeRuleId).snoozing).isFalse();
- assertThat(config.automaticRules.get(inactiveRuleId).snoozing).isFalse();
- } else {
- assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
- assertThat(config.automaticRules.get(activeRuleId).snoozing).isTrue();
- assertThat(config.automaticRules.get(inactiveRuleId).snoozing).isFalse();
+
+ // User turns DND off.
+ mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, origin.value(),
+ "snoozing", "systemui", Process.SYSTEM_UID);
+ ZenModeConfig config = mZenModeHelper.mConfig;
+ if (origin == ChangeOrigin.ORIGIN_USER) {
+ // Other rule was unaffected.
+ assertWithMessage("Failure for origin " + origin.name()).that(
+ mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+ assertWithMessage("Failure for origin " + origin.name()).that(
+ config.automaticRules.get(activeRuleId).snoozing).isFalse();
+ assertWithMessage("Failure for origin " + origin.name()).that(
+ config.automaticRules.get(inactiveRuleId).snoozing).isFalse();
+ } else {
+ assertWithMessage("Failure for origin " + origin.name()).that(
+ mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
+ assertWithMessage("Failure for origin " + origin.name()).that(
+ config.automaticRules.get(activeRuleId).snoozing).isTrue();
+ assertWithMessage("Failure for origin " + origin.name()).that(
+ config.automaticRules.get(inactiveRuleId).snoozing).isFalse();
+ }
}
}
@@ -3002,45 +3019,17 @@
assertEquals(ZEN_MODE_OFF, mZenModeHelper.mZenMode);
}
- private enum ModesFlag {
- MODES_UI(2, /* originForUserActionInSystemUi= */ UPDATE_ORIGIN_USER),
- MODES_API(1, /* originForUserActionInSystemUi= */ UPDATE_ORIGIN_USER),
- DISABLED(0, /* originForUserActionInSystemUi= */ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI);
-
- private final int mFlagsEnabled;
- @ConfigChangeOrigin
- private final int mOriginForUserActionInSystemUi;
-
- ModesFlag(int flagsEnabled, @ConfigChangeOrigin int originForUserActionInSystemUi) {
- this.mFlagsEnabled = flagsEnabled;
- this.mOriginForUserActionInSystemUi = originForUserActionInSystemUi;
- }
-
- void applyFlags(SetFlagsRule setFlagsRule) {
- if (mFlagsEnabled >= 1) {
- setFlagsRule.enableFlags(Flags.FLAG_MODES_API);
- } else {
- setFlagsRule.disableFlags(Flags.FLAG_MODES_API);
- }
- if (mFlagsEnabled >= 2) {
- setFlagsRule.enableFlags(Flags.FLAG_MODES_UI);
- } else {
- setFlagsRule.disableFlags(Flags.FLAG_MODES_UI);
- }
- }
- }
-
@Test
- public void testZenModeEventLog_setManualZenMode(@TestParameter ModesFlag modesFlag)
+ public void testZenModeEventLog_setManualZenMode()
throws IllegalArgumentException {
- modesFlag.applyFlags(mSetFlagsRule);
mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
setupZenConfig();
// Turn zen mode on (to important_interruptions)
// Need to additionally call the looper in order to finish the post-apply-config process
mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
- modesFlag.mOriginForUserActionInSystemUi, "", null, Process.SYSTEM_UID);
+ Flags.modesApi() ? UPDATE_ORIGIN_USER: UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", null,
+ Process.SYSTEM_UID);
// Now turn zen mode off, but via a different package UID -- this should get registered as
// "not an action by the user" because some other app is changing zen mode
@@ -3068,7 +3057,7 @@
assertEquals(DNDProtoEnums.MANUAL_RULE, mZenModeEventLogger.getChangedRuleType(0));
assertEquals(1, mZenModeEventLogger.getNumRulesActive(0));
assertThat(mZenModeEventLogger.getFromSystemOrSystemUi(0)).isEqualTo(
- modesFlag == ModesFlag.DISABLED);
+ !(Flags.modesUi() || Flags.modesApi()));
assertTrue(mZenModeEventLogger.getIsUserAction(0));
assertEquals(Process.SYSTEM_UID, mZenModeEventLogger.getPackageUid(0));
checkDndProtoMatchesSetupZenConfig(mZenModeEventLogger.getPolicyProto(0));
@@ -3097,9 +3086,8 @@
}
@Test
- public void testZenModeEventLog_automaticRules(@TestParameter ModesFlag modesFlag)
+ public void testZenModeEventLog_automaticRules()
throws IllegalArgumentException {
- modesFlag.applyFlags(mSetFlagsRule);
mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
setupZenConfig();
@@ -3116,15 +3104,14 @@
// Event 1: Mimic the rule coming on automatically by setting the Condition to STATE_TRUE
mZenModeHelper.setAutomaticZenRuleState(id,
new Condition(zenRule.getConditionId(), "", STATE_TRUE),
- UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI,
- Process.SYSTEM_UID);
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
// Event 2: "User" turns off the automatic rule (sets it to not enabled)
zenRule.setEnabled(false);
mZenModeHelper.updateAutomaticZenRule(id, zenRule,
- modesFlag.mOriginForUserActionInSystemUi, "", Process.SYSTEM_UID);
+ Flags.modesApi() ? UPDATE_ORIGIN_USER: UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "",
+ Process.SYSTEM_UID);
- // Add a new system rule
AutomaticZenRule systemRule = new AutomaticZenRule("systemRule",
null,
new ComponentName("android", "ScheduleConditionProvider"),
@@ -3132,7 +3119,8 @@
null,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String systemId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), systemRule,
- UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
+ Flags.modesApi() ? UPDATE_ORIGIN_USER: UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test",
+ Process.SYSTEM_UID);
// Event 3: turn on the system rule
mZenModeHelper.setAutomaticZenRuleState(systemId,
@@ -3140,9 +3128,9 @@
UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
// Event 4: "User" deletes the rule
- mZenModeHelper.removeAutomaticZenRule(systemId, modesFlag.mOriginForUserActionInSystemUi,
- "", Process.SYSTEM_UID);
-
+ mZenModeHelper.removeAutomaticZenRule(systemId,
+ Flags.modesApi() ? UPDATE_ORIGIN_USER: UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "",
+ Process.SYSTEM_UID);
// In total, this represents 4 events
assertEquals(4, mZenModeEventLogger.numLoggedChanges());
@@ -3201,7 +3189,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void testZenModeEventLog_automaticRuleActivatedFromAppByAppAndUser()
throws IllegalArgumentException {
mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
@@ -3288,22 +3276,19 @@
}
@Test
- public void testZenModeEventLog_policyChanges(@TestParameter ModesFlag modesFlag)
+ public void testZenModeEventLog_policyChanges()
throws IllegalArgumentException {
- modesFlag.applyFlags(mSetFlagsRule);
mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
setupZenConfig();
// First just turn zen mode on
mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
- modesFlag.mOriginForUserActionInSystemUi, "", null, Process.SYSTEM_UID);
+ UPDATE_ORIGIN_USER, "", null, Process.SYSTEM_UID);
// Now change the policy slightly; want to confirm that this'll be reflected in the logs
ZenModeConfig newConfig = mZenModeHelper.mConfig.copy();
- newConfig.allowAlarms = true;
- newConfig.allowRepeatCallers = false;
- mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(),
- modesFlag.mOriginForUserActionInSystemUi, Process.SYSTEM_UID);
+ mZenModeHelper.setNotificationPolicy(new Policy(PRIORITY_CATEGORY_ALARMS, 0, 0),
+ UPDATE_ORIGIN_USER, Process.SYSTEM_UID);
// Turn zen mode off; we want to make sure policy changes do not get logged when zen mode
// is off.
@@ -3311,10 +3296,8 @@
null, Process.SYSTEM_UID);
// Change the policy again
- newConfig.allowMessages = false;
- newConfig.allowRepeatCallers = true;
- mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(),
- modesFlag.mOriginForUserActionInSystemUi, Process.SYSTEM_UID);
+ mZenModeHelper.setNotificationPolicy(new Policy(PRIORITY_CATEGORY_REPEAT_CALLERS, 0, 0),
+ UPDATE_ORIGIN_USER, Process.SYSTEM_UID);
// Total events: we only expect ones for turning on, changing policy, and turning off
assertEquals(3, mZenModeEventLogger.numLoggedChanges());
@@ -3347,9 +3330,7 @@
}
@Test
- public void testZenModeEventLog_ruleCounts(@TestParameter ModesFlag modesFlag)
- throws IllegalArgumentException {
- modesFlag.applyFlags(mSetFlagsRule);
+ public void testZenModeEventLog_ruleCounts() throws IllegalArgumentException {
mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
setupZenConfig();
@@ -3374,14 +3355,12 @@
// Rule 3, has stricter settings than the default settings
ZenModeConfig ruleConfig = mZenModeHelper.mConfig.copy();
- ruleConfig.allowReminders = false;
- ruleConfig.allowCalls = false;
- ruleConfig.allowMessages = false;
+ ruleConfig.applyNotificationPolicy(new Policy(0, 0, 0));
AutomaticZenRule zenRule3 = new AutomaticZenRule("name3",
null,
new ComponentName("android", "ScheduleConditionProvider"),
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
- ruleConfig.toZenPolicy(),
+ ruleConfig.getZenPolicy(),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id3 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule3,
UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
@@ -3452,10 +3431,8 @@
}
@Test
- public void testZenModeEventLog_noLogWithNoConfigChange(
- @TestParameter ModesFlag modesFlag) throws IllegalArgumentException {
+ public void testZenModeEventLog_noLogWithNoConfigChange() throws IllegalArgumentException {
// If evaluateZenMode is called independently of a config change, don't log.
- modesFlag.applyFlags(mSetFlagsRule);
mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
setupZenConfig();
@@ -3472,17 +3449,16 @@
}
@Test
- public void testZenModeEventLog_reassignUid(@TestParameter ModesFlag modesFlag)
+ public void testZenModeEventLog_reassignUid()
throws IllegalArgumentException {
// Test that, only in specific cases, we reassign the calling UID to one associated with
// the automatic rule owner.
- modesFlag.applyFlags(mSetFlagsRule);
mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
setupZenConfig();
// Explicitly set up all rules with the same policy as the manual rule so there will be
// no policy changes in this test case.
- ZenPolicy manualRulePolicy = mZenModeHelper.mConfig.toZenPolicy();
+ ZenPolicy manualRulePolicy = mZenModeHelper.mConfig.getZenPolicy();
// Rule 1, owned by a package
AutomaticZenRule zenRule = new AutomaticZenRule("name",
@@ -3502,7 +3478,7 @@
manualRulePolicy,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2,
- modesFlag.mOriginForUserActionInSystemUi, "test", Process.SYSTEM_UID);
+ UPDATE_ORIGIN_USER, "test", Process.SYSTEM_UID);
// Turn on rule 1; call looks like it's from the system. Because setting a condition is
// typically an automatic (non-user-initiated) action, expect the calling UID to be
@@ -3521,7 +3497,7 @@
// from the system-provided one.
zenRule.setEnabled(false);
mZenModeHelper.updateAutomaticZenRule(id, zenRule,
- modesFlag.mOriginForUserActionInSystemUi, "", Process.SYSTEM_UID);
+ UPDATE_ORIGIN_USER, "", Process.SYSTEM_UID);
// Add a manual rule. Any manual rule changes should not get calling uids reassigned.
mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, UPDATE_ORIGIN_APP,
@@ -3578,10 +3554,8 @@
}
@Test
- public void testZenModeEventLog_channelsBypassingChanges(
- @TestParameter ModesFlag modesFlag) {
+ public void testZenModeEventLog_channelsBypassingChanges() {
// Verify that the right thing happens when the canBypassDnd value changes.
- modesFlag.applyFlags(mSetFlagsRule);
mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
setupZenConfig();
@@ -3589,21 +3563,25 @@
// as a user action, and *should* get its UID reassigned.
mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", CUSTOM_PKG_NAME, Process.SYSTEM_UID);
+ assertEquals(1, mZenModeEventLogger.numLoggedChanges());
// Now change apps bypassing to true
ZenModeConfig newConfig = mZenModeHelper.mConfig.copy();
newConfig.areChannelsBypassingDnd = true;
mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(),
UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
+ assertEquals(2, mZenModeEventLogger.numLoggedChanges());
// and then back to false, all without changing anything else
newConfig.areChannelsBypassingDnd = false;
mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(),
UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
+ assertEquals(3, mZenModeEventLogger.numLoggedChanges());
// Turn off manual mode, call from a package: don't reset UID even though enabler is set
mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, UPDATE_ORIGIN_APP, "",
CUSTOM_PKG_NAME, 12345);
+ assertEquals(4, mZenModeEventLogger.numLoggedChanges());
// And likewise when turning it back on again
mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, UPDATE_ORIGIN_APP,
@@ -3642,11 +3620,11 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void testZenModeEventLog_policyAllowChannels() {
// when modes_api flag is on, ensure that any change in allow_channels gets logged,
// even when there are no other changes.
mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
// Default zen config has allow channels = priority (aka on)
setupZenConfig();
@@ -3658,8 +3636,12 @@
// Now change only the channels part of the policy; want to confirm that this'll be
// reflected in the logs
ZenModeConfig newConfig = mZenModeHelper.mConfig.copy();
- newConfig.allowPriorityChannels = false;
- mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(),
+ Policy oldPolicy = newConfig.toNotificationPolicy();
+ Policy newPolicy = new Policy(oldPolicy.priorityCategories, oldPolicy.priorityCallSenders,
+ oldPolicy.priorityMessageSenders, oldPolicy.suppressedVisualEffects,
+ STATE_PRIORITY_CHANNELS_BLOCKED,
+ oldPolicy.priorityConversationSenders);
+ mZenModeHelper.setNotificationPolicy(newPolicy,
UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
// Total events: one for turning on, one for changing policy
@@ -3688,7 +3670,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void testZenModeEventLog_ruleWithInterruptionFilterAll_notLoggedAsDndChange() {
mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
setupZenConfig();
@@ -3730,7 +3712,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void testZenModeEventLog_activeRuleTypes() {
mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
setupZenConfig();
@@ -3748,7 +3730,7 @@
// Create immersive rule
AutomaticZenRule immersive = new AutomaticZenRule.Builder("Immersed", CONDITION_ID)
.setType(TYPE_IMMERSIVE)
- .setZenPolicy(mZenModeHelper.mConfig.toZenPolicy()) // same as the manual rule
+ .setZenPolicy(mZenModeHelper.mConfig.getZenPolicy()) // same as the manual rule
.build();
String immersiveId = mZenModeHelper.addAutomaticZenRule(mPkg, immersive, UPDATE_ORIGIN_APP,
"reason", CUSTOM_PKG_UID);
@@ -3819,10 +3801,9 @@
}
@Test
- @DisableFlags(Flags.FLAG_MODES_API)
+ @DisableFlags(FLAG_MODES_API)
public void testUpdateConsolidatedPolicy_preModesApiDefaultRulesOnly_takesGlobalDefault() {
setupZenConfig();
-
// When there's one automatic rule active and it doesn't specify a policy, test that the
// resulting consolidated policy is one that matches the default rule settings.
AutomaticZenRule zenRule = new AutomaticZenRule("name",
@@ -3839,6 +3820,9 @@
new Condition(zenRule.getConditionId(), "", STATE_TRUE),
UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
+ assertEquals(mZenModeHelper.getNotificationPolicy(),
+ mZenModeHelper.getConsolidatedNotificationPolicy());
+
// inspect the consolidated policy. Based on setupZenConfig() values.
assertFalse(mZenModeHelper.mConsolidatedPolicy.allowAlarms());
assertFalse(mZenModeHelper.mConsolidatedPolicy.allowMedia());
@@ -3853,9 +3837,7 @@
}
@Test
- public void testUpdateConsolidatedPolicy_modesApiDefaultRulesOnly_takesDefault(
- @TestParameter({"MODES_UI", "MODES_API"}) ModesFlag modesFlag) {
- modesFlag.applyFlags(mSetFlagsRule);
+ public void testUpdateConsolidatedPolicy_modesApiDefaultRulesOnly_takesDefault() {
setupZenConfig();
// When there's one automatic rule active and it doesn't specify a policy, test that the
@@ -3876,13 +3858,13 @@
// inspect the consolidated policy, which should match the device default settings.
assertThat(ZenAdapters.notificationPolicyToZenPolicy(mZenModeHelper.mConsolidatedPolicy))
- .isEqualTo(modesFlag == ModesFlag.MODES_UI
+ .isEqualTo(Flags.modesUi()
? mZenModeHelper.getDefaultZenPolicy()
- : mZenModeHelper.mConfig.toZenPolicy());
+ : mZenModeHelper.mConfig.getZenPolicy());
}
@Test
- @DisableFlags(Flags.FLAG_MODES_API)
+ @DisableFlags(FLAG_MODES_API)
public void testUpdateConsolidatedPolicy_preModesApiCustomPolicyOnly_fillInWithGlobal() {
setupZenConfig();
@@ -3928,9 +3910,8 @@
}
@Test
- public void testUpdateConsolidatedPolicy_modesApiCustomPolicyOnly_fillInWithDefault(
- @TestParameter({"MODES_UI", "MODES_API"}) ModesFlag modesFlag) {
- modesFlag.applyFlags(mSetFlagsRule);
+ @EnableFlags(FLAG_MODES_API)
+ public void testUpdateConsolidatedPolicy_modesApiCustomPolicyOnly_fillInWithDefault() {
setupZenConfig();
// when there's only one automatic rule active and it has a custom policy, make sure that's
@@ -3962,12 +3943,12 @@
// policy for every field specified, and take default values (from either device default
// policy or manual rule) for unspecified things
assertThat(mZenModeHelper.mConsolidatedPolicy.allowAlarms()).isEqualTo(
- modesFlag == ModesFlag.MODES_UI ? true : false); // default
+ Flags.modesUi() ? true : false); // default
assertThat(mZenModeHelper.mConsolidatedPolicy.allowMedia()).isEqualTo(
- modesFlag == ModesFlag.MODES_UI ? true : false); // default
+ Flags.modesUi() ? true : false); // default
assertThat(mZenModeHelper.mConsolidatedPolicy.allowSystem()).isTrue(); // custom
assertThat(mZenModeHelper.mConsolidatedPolicy.allowReminders()).isEqualTo(
- modesFlag == ModesFlag.MODES_UI ? false : true); // default
+ Flags.modesUi() ? false : true); // default
assertThat(mZenModeHelper.mConsolidatedPolicy.allowCalls()).isFalse(); // custom
assertThat(mZenModeHelper.mConsolidatedPolicy.allowMessages()).isTrue(); // default
assertThat(mZenModeHelper.mConsolidatedPolicy.allowRepeatCallers()).isFalse(); // custom
@@ -3976,7 +3957,7 @@
}
@Test
- @DisableFlags(Flags.FLAG_MODES_API)
+ @DisableFlags(FLAG_MODES_API)
public void testUpdateConsolidatedPolicy_preModesApiDefaultAndCustomActive_mergesWithGlobal() {
setupZenConfig();
@@ -4037,9 +4018,8 @@
}
@Test
- public void testUpdateConsolidatedPolicy_modesApiDefaultAndCustomActive_mergesWithDefault(
- @TestParameter({"MODES_UI", "MODES_API"}) ModesFlag modesFlag) {
- modesFlag.applyFlags(mSetFlagsRule);
+ @EnableFlags(FLAG_MODES_API)
+ public void testUpdateConsolidatedPolicy_modesApiDefaultAndCustomActive_mergesWithDefault() {
setupZenConfig();
// when there are two rules active, one inheriting the default policy and one setting its
@@ -4088,10 +4068,10 @@
// restrictive option of each of the two
assertThat(mZenModeHelper.mConsolidatedPolicy.allowAlarms()).isFalse(); // custom stricter
assertThat(mZenModeHelper.mConsolidatedPolicy.allowMedia()).isEqualTo(
- modesFlag == ModesFlag.MODES_UI ? true : false); // default
+ Flags.modesUi() ? true : false); // default
assertThat(mZenModeHelper.mConsolidatedPolicy.allowSystem()).isFalse(); // default stricter
assertThat(mZenModeHelper.mConsolidatedPolicy.allowReminders()).isEqualTo(
- modesFlag == ModesFlag.MODES_UI ? false : true); // default
+ Flags.modesUi() ? false : true); // default
assertThat(mZenModeHelper.mConsolidatedPolicy.allowCalls()).isFalse(); // custom stricter
assertThat(mZenModeHelper.mConsolidatedPolicy.allowMessages()).isTrue(); // default
assertThat(mZenModeHelper.mConsolidatedPolicy.allowConversations()).isTrue(); // default
@@ -4099,12 +4079,12 @@
.isFalse(); // custom stricter
assertThat(mZenModeHelper.mConsolidatedPolicy.showBadges()).isFalse(); // custom stricter
assertThat(mZenModeHelper.mConsolidatedPolicy.showPeeking()).isEqualTo(
- modesFlag == ModesFlag.MODES_UI ? false : true); // default
+ Flags.modesUi() ? false : true); // default
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void testUpdateConsolidatedPolicy_allowChannels() {
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
setupZenConfig();
// one rule, custom policy, allows channels
@@ -4153,9 +4133,8 @@
}
@Test
- public void testUpdateConsolidatedPolicy_ignoresActiveRulesWithInterruptionFilterAll(
- @TestParameter({"MODES_UI", "MODES_API"}) ModesFlag modesFlag) {
- modesFlag.applyFlags(mSetFlagsRule);
+ @EnableFlags(FLAG_MODES_API)
+ public void testUpdateConsolidatedPolicy_ignoresActiveRulesWithInterruptionFilterAll() {
setupZenConfig();
// Rules with INTERRUPTION_FILTER_ALL are skipped when calculating consolidated policy.
@@ -4193,11 +4172,11 @@
// Consolidated Policy should be default + rule1.
assertThat(mZenModeHelper.mConsolidatedPolicy.allowAlarms()).isEqualTo(
- modesFlag == ModesFlag.MODES_UI ? true : false); // default
+ Flags.modesUi() ? true : false); // default
assertThat(mZenModeHelper.mConsolidatedPolicy.allowMedia()).isTrue(); // priority rule
assertThat(mZenModeHelper.mConsolidatedPolicy.allowSystem()).isTrue(); // priority rule
assertThat(mZenModeHelper.mConsolidatedPolicy.allowReminders()).isEqualTo(
- modesFlag == ModesFlag.MODES_UI ? false : true); // default
+ Flags.modesUi() ? false : true); // default
assertThat(mZenModeHelper.mConsolidatedPolicy.allowCalls()).isTrue(); // default
assertThat(mZenModeHelper.mConsolidatedPolicy.allowMessages()).isTrue(); // default
assertThat(mZenModeHelper.mConsolidatedPolicy.allowConversations()).isTrue(); // default
@@ -4205,8 +4184,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void zenRuleToAutomaticZenRule_allFields() {
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(
new String[]{OWNER.getPackageName()});
@@ -4249,8 +4228,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void automaticZenRuleToZenRule_allFields() {
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(
new String[]{OWNER.getPackageName()});
@@ -4291,7 +4270,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void updateAutomaticZenRule_fromApp_updatesNameUnlessUserModified() {
// Add a starting rule with the name OriginalName.
AutomaticZenRule azrBase = new AutomaticZenRule.Builder("OriginalName", CONDITION_ID)
@@ -4348,7 +4327,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void updateAutomaticZenRule_fromUser_updatesBitmaskAndValue() {
// Adds a starting rule with empty zen policies and device effects
AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
@@ -4385,7 +4364,7 @@
assertThat(rule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_PRIORITY);
assertThat(rule.getIconResId()).isEqualTo(ICON_RES_ID);
assertThat(rule.getZenPolicy().getPriorityChannelsAllowed()).isEqualTo(
- ZenPolicy.STATE_DISALLOW);
+ STATE_DISALLOW);
assertThat(rule.getDeviceEffects().shouldDisplayGrayscale()).isTrue();
@@ -4401,7 +4380,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void updateAutomaticZenRule_fromSystemUi_updatesValues() {
// Adds a starting rule with empty zen policies and device effects
AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
@@ -4441,7 +4420,7 @@
// UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI should change the value but NOT update the bitmask.
assertThat(rule.getIconResId()).isEqualTo(ICON_RES_ID);
assertThat(rule.getZenPolicy().getPriorityCategoryReminders())
- .isEqualTo(ZenPolicy.STATE_ALLOW);
+ .isEqualTo(STATE_ALLOW);
assertThat(rule.getDeviceEffects().shouldDisplayGrayscale()).isTrue();
ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
@@ -4451,7 +4430,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void updateAutomaticZenRule_fromApp_updatesValuesIfRuleNotUserModified() {
// Adds a starting rule with empty zen policies and device effects
AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
@@ -4490,7 +4469,7 @@
assertThat(storedRule.zenMode).isEqualTo(ZEN_MODE_ALARMS);
assertThat(storedRule.zenPolicy.getPriorityCategoryReminders())
- .isEqualTo(ZenPolicy.STATE_ALLOW);
+ .isEqualTo(STATE_ALLOW);
assertThat(storedRule.zenDeviceEffects.shouldDisplayGrayscale()).isTrue();
assertThat(storedRule.userModifiedFields).isEqualTo(0);
assertThat(storedRule.zenPolicyUserModifiedFields).isEqualTo(0);
@@ -4514,7 +4493,7 @@
// so the rule is not changed, and neither is the bitmask.
assertThat(ruleUser.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALL);
assertThat(ruleUser.getZenPolicy().getPriorityCategoryReminders())
- .isEqualTo(ZenPolicy.STATE_DISALLOW);
+ .isEqualTo(STATE_DISALLOW);
assertThat(ruleUser.getDeviceEffects().shouldDisplayGrayscale()).isFalse();
storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleIdUser);
@@ -4525,7 +4504,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void addAutomaticZenRule_updatesValues() {
// Adds a starting rule with empty zen policies and device effects
AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
@@ -4543,7 +4522,7 @@
// The values are modified but the bitmask is not.
assertThat(rule.getZenPolicy().getPriorityCategoryReminders())
- .isEqualTo(ZenPolicy.STATE_ALLOW);
+ .isEqualTo(STATE_ALLOW);
assertThat(rule.getDeviceEffects().shouldDisplayGrayscale()).isTrue();
ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
@@ -4551,7 +4530,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void updateAutomaticZenRule_nullDeviceEffectsUpdate() {
// Adds a starting rule with empty zen policies and device effects
ZenDeviceEffects zde = new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build();
@@ -4578,7 +4557,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void updateAutomaticZenRule_nullPolicyUpdate() {
// Adds a starting rule with set zen policy and empty device effects
AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
@@ -4607,7 +4586,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void automaticZenRuleToZenRule_nullToNonNullPolicyUpdate() {
when(mContext.checkCallingPermission(anyString()))
.thenReturn(PackageManager.PERMISSION_GRANTED);
@@ -4654,7 +4633,7 @@
// New ZenPolicy differs from the default config
assertThat(rule.getZenPolicy()).isNotNull();
assertThat(rule.getZenPolicy().getPriorityChannelsAllowed()).isEqualTo(
- ZenPolicy.STATE_DISALLOW);
+ STATE_DISALLOW);
ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
assertThat(storedRule.canBeUpdatedByApp()).isFalse();
@@ -4671,7 +4650,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void automaticZenRuleToZenRule_nullToNonNullDeviceEffectsUpdate() {
// Adds a starting rule with empty zen policies and device effects
AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
@@ -4775,8 +4754,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void testUpdateAutomaticRule_activated_triggersBroadcast() throws Exception {
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
setupZenConfig();
// Add a new automatic zen rule that's enabled
@@ -4815,8 +4794,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void testUpdateAutomaticRule_deactivatedByUser_triggersBroadcast() throws Exception {
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
setupZenConfig();
// Add a new automatic zen rule that's enabled
@@ -4860,8 +4839,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void testUpdateAutomaticRule_deactivatedByApp_triggersBroadcast() throws Exception {
- mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
setupZenConfig();
// Add a new automatic zen rule that's enabled
@@ -4937,7 +4916,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void updateAutomaticZenRule_ruleChanged_deactivatesRule() {
assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", CONDITION_ID)
@@ -4961,7 +4940,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void updateAutomaticZenRule_ruleNotChanged_doesNotDeactivateRule() {
assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", CONDITION_ID)
@@ -4984,7 +4963,7 @@
}
@Test
- @EnableFlags({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI})
+ @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
public void updateAutomaticZenRule_ruleChangedByUser_doesNotDeactivateRule() {
assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", CONDITION_ID)
@@ -5009,7 +4988,7 @@
}
@Test
- @EnableFlags({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI})
+ @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
public void updateAutomaticZenRule_ruleDisabledByUser_doesNotReactivateOnReenable() {
assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", CONDITION_ID)
@@ -5034,7 +5013,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void removeAutomaticZenRule_propagatesOriginToEffectsApplier() {
mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
reset(mDeviceEffectsApplier);
@@ -5057,8 +5036,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void testDeviceEffects_applied() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(UPDATE_ORIGIN_INIT));
@@ -5077,8 +5056,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void testDeviceEffects_onDeactivateRule_applied() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
ZenDeviceEffects zde = new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build();
@@ -5096,8 +5075,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void testDeviceEffects_changeToConsolidatedEffects_applied() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(UPDATE_ORIGIN_INIT));
@@ -5136,8 +5115,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void testDeviceEffects_noChangeToConsolidatedEffects_notApplied() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(UPDATE_ORIGIN_INIT));
@@ -5161,9 +5140,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void testDeviceEffects_activeBeforeApplierProvided_appliedWhenProvided() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
-
ZenDeviceEffects zde = new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build();
String ruleId = addRuleWithEffects(zde);
verify(mDeviceEffectsApplier, never()).apply(any(), anyInt());
@@ -5178,8 +5156,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void testDeviceEffects_onUserSwitch_appliedImmediately() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(UPDATE_ORIGIN_INIT));
@@ -5214,9 +5192,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void removeAndAddAutomaticZenRule_wasCustomized_isRestored() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
-
// Start with a rule.
mZenModeHelper.mConfig.automaticRules.clear();
mTestClock.setNowMillis(1000);
@@ -5257,7 +5234,7 @@
assertThat(newRuleId).isEqualTo(ruleId);
assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALARMS);
assertThat(finalRule.getZenPolicy().getPriorityCategoryRepeatCallers()).isEqualTo(
- ZenPolicy.STATE_ALLOW);
+ STATE_ALLOW);
ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
assertThat(storedRule.userModifiedFields).isEqualTo(
@@ -5270,9 +5247,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void removeAndAddAutomaticZenRule_wasNotCustomized_isNotRestored() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
-
// Start with a single rule.
mZenModeHelper.mConfig.automaticRules.clear();
mTestClock.setNowMillis(1000);
@@ -5303,9 +5279,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void removeAndAddAutomaticZenRule_recreatedButNotByApp_isNotRestored() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
-
// Start with a single rule.
mZenModeHelper.mConfig.automaticRules.clear();
mTestClock.setNowMillis(1000);
@@ -5345,16 +5320,15 @@
assertThat(newRuleId).isNotEqualTo(ruleId);
assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_PRIORITY);
assertThat(finalRule.getZenPolicy().getPriorityCategoryRepeatCallers()).isEqualTo(
- ZenPolicy.STATE_DISALLOW);
+ STATE_DISALLOW);
// Also, we discarded the "deleted rule" since we're not interested in recreating it.
assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(0);
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void removeAndAddAutomaticZenRule_removedByUser_isNotRestored() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
-
// Start with a single rule.
mZenModeHelper.mConfig.automaticRules.clear();
mTestClock.setNowMillis(1000);
@@ -5394,8 +5368,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void removeAutomaticZenRule_preservedForRestoringByPackageAndConditionId() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
mContext.getTestablePermissions().setPermission(Manifest.permission.MANAGE_NOTIFICATIONS,
PERMISSION_GRANTED); // So that canManageAZR passes although packages don't match.
mZenModeHelper.mConfig.automaticRules.clear();
@@ -5434,8 +5408,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void removeAllZenRules_preservedForRestoring() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
mZenModeHelper.mConfig.automaticRules.clear();
mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
@@ -5456,8 +5430,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void removeAllZenRules_fromSystem_deletesPreservedRulesToo() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
mZenModeHelper.mConfig.automaticRules.clear();
// Start with deleted rules from 2 different packages.
@@ -5476,7 +5450,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void removeAndAddAutomaticZenRule_wasActive_isRestoredAsInactive() {
// Start with a rule.
mZenModeHelper.mConfig.automaticRules.clear();
@@ -5525,7 +5499,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void removeAndAddAutomaticZenRule_wasSnoozed_isRestoredAsInactive() {
// Start with a rule.
mZenModeHelper.mConfig.automaticRules.clear();
@@ -5579,8 +5553,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void testRuleCleanup() throws Exception {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
Instant now = Instant.ofEpochMilli(1701796461000L);
Instant yesterday = now.minus(1, ChronoUnit.DAYS);
Instant aWeekAgo = now.minus(7, ChronoUnit.DAYS);
@@ -5637,7 +5611,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void getAutomaticZenRuleState_ownedRule_returnsRuleState() {
String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
new AutomaticZenRule.Builder("Rule", CONDITION_ID)
@@ -5662,7 +5636,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void getAutomaticZenRuleState_notOwnedRule_returnsStateUnknown() {
// Assume existence of a system-owned rule that is currently ACTIVE.
ZenRule systemRule = newZenRule("android", Instant.now(), null);
@@ -5678,7 +5652,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void setAutomaticZenRuleState_idForNotOwnedRule_ignored() {
// Assume existence of an other-package-owned rule that is currently ACTIVE.
assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
@@ -5699,7 +5673,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void setAutomaticZenRuleState_conditionForNotOwnedRule_ignored() {
// Assume existence of an other-package-owned rule that is currently ACTIVE.
assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
@@ -5720,7 +5694,7 @@
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void testCallbacks_policy() throws Exception {
setupZenConfig();
assertThat(mZenModeHelper.getNotificationPolicy().allowReminders()).isTrue();
@@ -5740,10 +5714,9 @@
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void testCallbacks_consolidatedPolicy() throws Exception {
- setupZenConfig();
- assertThat(mZenModeHelper.getConsolidatedNotificationPolicy().allowAlarms()).isTrue();
+ assertThat(mZenModeHelper.getConsolidatedNotificationPolicy().allowMedia()).isTrue();
SettableFuture<Policy> futureConsolidatedPolicy = SettableFuture.create();
mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
@Override
@@ -5762,12 +5735,12 @@
new Condition(CONDITION_ID, "", STATE_TRUE), UPDATE_ORIGIN_APP, CUSTOM_PKG_UID);
Policy callbackPolicy = futureConsolidatedPolicy.get(1, TimeUnit.SECONDS);
- assertThat(callbackPolicy.allowAlarms()).isFalse();
+ assertThat(callbackPolicy.allowMedia()).isFalse();
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void applyGlobalZenModeAsImplicitZenRule_createsImplicitRuleAndActivatesIt() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
mZenModeHelper.mConfig.automaticRules.clear();
mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
@@ -5777,13 +5750,13 @@
.comparingElementsUsing(IGNORE_METADATA)
.containsExactly(
expectedImplicitRule(CUSTOM_PKG_NAME, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
- mZenModeHelper.mConfig.toZenPolicy(), // copy of global config
+ mZenModeHelper.mConfig.getZenPolicy(), // copy of global config
true));
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void applyGlobalZenModeAsImplicitZenRule_updatesImplicitRuleAndActivatesIt() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
mZenModeHelper.mConfig.automaticRules.clear();
mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
@@ -5798,12 +5771,12 @@
.comparingElementsUsing(IGNORE_METADATA)
.containsExactly(
expectedImplicitRule(CUSTOM_PKG_NAME, ZEN_MODE_ALARMS,
- mZenModeHelper.mConfig.toZenPolicy(), // copy of global config
+ mZenModeHelper.mConfig.getZenPolicy(), // copy of global config
true));
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void applyGlobalZenModeAsImplicitZenRule_ruleCustomized_doesNotUpdateRule() {
mZenModeHelper.mConfig.automaticRules.clear();
String pkg = mContext.getPackageName();
@@ -5835,7 +5808,7 @@
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void applyGlobalZenModeAsImplicitZenRule_ruleCustomizedButNotFilter_updatesRule() {
mZenModeHelper.mConfig.automaticRules.clear();
String pkg = mContext.getPackageName();
@@ -5866,8 +5839,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void applyGlobalZenModeAsImplicitZenRule_modeOff_deactivatesImplicitRule() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
mZenModeHelper.mConfig.automaticRules.clear();
mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(mPkg, CUSTOM_PKG_UID,
ZEN_MODE_IMPORTANT_INTERRUPTIONS);
@@ -5883,8 +5856,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void applyGlobalZenModeAsImplicitZenRule_modeOffButNoPreviousRule_ignored() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
mZenModeHelper.mConfig.automaticRules.clear();
mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
@@ -5894,8 +5867,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void applyGlobalZenModeAsImplicitZenRule_update_unsnoozesRule() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
mZenModeHelper.mConfig.automaticRules.clear();
mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
@@ -5915,8 +5888,8 @@
}
@Test
+ @DisableFlags(FLAG_MODES_API)
public void applyGlobalZenModeAsImplicitZenRule_flagOff_ignored() {
- mSetFlagsRule.disableFlags(android.app.Flags.FLAG_MODES_API);
mZenModeHelper.mConfig.automaticRules.clear();
withoutWtfCrash(
@@ -5928,8 +5901,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void applyGlobalPolicyAsImplicitZenRule_createsImplicitRule() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
mZenModeHelper.mConfig.automaticRules.clear();
Policy policy = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS,
@@ -5952,8 +5925,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void applyGlobalPolicyAsImplicitZenRule_updatesImplicitRule() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
mZenModeHelper.mConfig.automaticRules.clear();
Policy original = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS,
@@ -5983,7 +5956,7 @@
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void applyGlobalPolicyAsImplicitZenRule_ruleCustomized_doesNotUpdateRule() {
mZenModeHelper.mConfig.automaticRules.clear();
String pkg = mContext.getPackageName();
@@ -5995,7 +5968,7 @@
// Store this for checking later.
ZenPolicy originalEffectiveZenPolicy = new ZenPolicy.Builder(
- mZenModeHelper.mConfig.toZenPolicy()).allowMedia(true).build();
+ mZenModeHelper.mConfig.getZenPolicy()).allowMedia(true).build();
// From user, update that rule's policy.
AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
@@ -6023,7 +5996,7 @@
}
@Test
- @EnableFlags(android.app.Flags.FLAG_MODES_API)
+ @EnableFlags(FLAG_MODES_API)
public void applyGlobalPolicyAsImplicitZenRule_ruleCustomizedButNotZenPolicy_updatesRule() {
mZenModeHelper.mConfig.automaticRules.clear();
String pkg = mContext.getPackageName();
@@ -6035,7 +6008,7 @@
// Store this for checking later.
ZenPolicy originalEffectiveZenPolicy = new ZenPolicy.Builder(
- mZenModeHelper.mConfig.toZenPolicy()).allowMedia(true).build();
+ mZenModeHelper.mConfig.getZenPolicy()).allowMedia(true).build();
// From user, update something in that rule, but not the ZenPolicy.
AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
@@ -6060,8 +6033,8 @@
}
@Test
+ @DisableFlags(FLAG_MODES_API)
public void applyGlobalPolicyAsImplicitZenRule_flagOff_ignored() {
- mSetFlagsRule.disableFlags(android.app.Flags.FLAG_MODES_API);
mZenModeHelper.mConfig.automaticRules.clear();
withoutWtfCrash(
@@ -6072,8 +6045,8 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void getNotificationPolicyFromImplicitZenRule_returnsSetPolicy() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
Policy writtenPolicy = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS,
PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED,
Policy.getAllSuppressedVisualEffects(), STATE_FALSE,
@@ -6088,32 +6061,30 @@
}
@Test
+ @EnableFlags(FLAG_MODES_API)
+ @DisableFlags(FLAG_MODES_UI)
public void getNotificationPolicyFromImplicitZenRule_ruleWithoutPolicy_copiesGlobalPolicy() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
- mZenModeHelper.mConfig.allowCalls = true;
- mZenModeHelper.mConfig.allowConversations = false;
-
// Implicit rule will get the global policy at the time of rule creation.
mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
- ZEN_MODE_ALARMS);
+ ZEN_MODE_IMPORTANT_INTERRUPTIONS);
- // If the policy then changes afterwards, we should keep the snapshotted version.
- mZenModeHelper.mConfig.allowCalls = false;
-
+ // If the policy then changes afterwards, it should inherit updates because user cannot
+ // edit the policy in the UI.
+ mZenModeHelper.setNotificationPolicy(new Policy(PRIORITY_CATEGORY_ALARMS, 0, 0),
+ UPDATE_ORIGIN_APP, 1);
Policy readPolicy = mZenModeHelper.getNotificationPolicyFromImplicitZenRule(
CUSTOM_PKG_NAME);
assertThat(readPolicy).isNotNull();
- assertThat(readPolicy.allowCalls()).isTrue();
- assertThat(readPolicy.allowConversations()).isFalse();
+ assertThat(readPolicy.allowCalls()).isFalse();
+ assertThat(readPolicy.allowAlarms()).isTrue();
}
@Test
+ @EnableFlags(FLAG_MODES_API)
public void getNotificationPolicyFromImplicitZenRule_noImplicitRule_returnsGlobalPolicy() {
- mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
-
- mZenModeHelper.mConfig.allowCalls = true;
- mZenModeHelper.mConfig.allowConversations = false;
+ Policy policy = new Policy(PRIORITY_CATEGORY_CALLS, PRIORITY_SENDERS_STARRED, 0);
+ mZenModeHelper.setNotificationPolicy(policy, UPDATE_ORIGIN_USER, 1);
Policy readPolicy = mZenModeHelper.getNotificationPolicyFromImplicitZenRule(
CUSTOM_PKG_NAME);
@@ -6124,8 +6095,8 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_API)
- @DisableFlags(Flags.FLAG_MODES_UI)
+ @EnableFlags(FLAG_MODES_API)
+ @DisableFlags(FLAG_MODES_UI)
public void setNotificationPolicy_updatesRulePolicies_ifRulePolicyIsDefaultOrGlobalPolicy() {
ZenPolicy defaultZenPolicy = mZenModeHelper.getDefaultZenPolicy();
Policy previousManualPolicy = mZenModeHelper.mConfig.toNotificationPolicy();
@@ -6185,6 +6156,35 @@
assertThat(storedRule.getIconResId()).isEqualTo(0);
}
+ @Test
+ @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ public void setManualZenRuleDeviceEffects_noPreexistingMode() {
+ ZenDeviceEffects effects = new ZenDeviceEffects.Builder()
+ .setShouldDimWallpaper(true)
+ .build();
+ mZenModeHelper.setManualZenRuleDeviceEffects(effects, UPDATE_ORIGIN_USER, "settings", 1000);
+
+ assertThat(mZenModeHelper.getConfig().manualRule).isNotNull();
+ assertThat(mZenModeHelper.getConfig().isManualActive()).isFalse();
+ assertThat(mZenModeHelper.getConfig().manualRule.zenDeviceEffects).isEqualTo(effects);
+ }
+
+ @Test
+ @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ public void setManualZenRuleDeviceEffects_preexistingMode() {
+ mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, Uri.EMPTY, UPDATE_ORIGIN_USER,
+ "create manual rule", "settings", 1000);
+
+ ZenDeviceEffects effects = new ZenDeviceEffects.Builder()
+ .setShouldDimWallpaper(true)
+ .build();
+ mZenModeHelper.setManualZenRuleDeviceEffects(effects, UPDATE_ORIGIN_USER, "settings", 1000);
+
+ assertThat(mZenModeHelper.getConfig().manualRule).isNotNull();
+ assertThat(mZenModeHelper.getConfig().isManualActive()).isFalse();
+ assertThat(mZenModeHelper.getConfig().manualRule.zenDeviceEffects).isEqualTo(effects);
+ }
+
private static void addZenRule(ZenModeConfig config, String id, String ownerPkg, int zenMode,
@Nullable ZenPolicy zenPolicy) {
ZenRule rule = new ZenRule();
@@ -6246,34 +6246,19 @@
// TODO: b/310620812 - Update setup methods to include allowChannels() when MODES_API is inlined
private void setupZenConfig() {
- mZenModeHelper.mZenMode = ZEN_MODE_OFF;
- mZenModeHelper.mConfig.allowAlarms = false;
- mZenModeHelper.mConfig.allowMedia = false;
- mZenModeHelper.mConfig.allowSystem = false;
- mZenModeHelper.mConfig.allowReminders = true;
- mZenModeHelper.mConfig.allowCalls = true;
- mZenModeHelper.mConfig.allowCallsFrom = PRIORITY_SENDERS_STARRED;
- mZenModeHelper.mConfig.allowMessages = true;
- mZenModeHelper.mConfig.allowConversations = true;
- mZenModeHelper.mConfig.allowEvents = true;
- mZenModeHelper.mConfig.allowRepeatCallers = true;
- mZenModeHelper.mConfig.suppressedVisualEffects = SUPPRESSED_EFFECT_BADGE;
- mZenModeHelper.mConfig.manualRule = null;
- }
-
- private void setupZenConfigMaintained() {
- // config is still the same as when it was setup (setupZenConfig)
- assertFalse(mZenModeHelper.mConfig.allowAlarms);
- assertFalse(mZenModeHelper.mConfig.allowMedia);
- assertFalse(mZenModeHelper.mConfig.allowSystem);
- assertTrue(mZenModeHelper.mConfig.allowReminders);
- assertTrue(mZenModeHelper.mConfig.allowCalls);
- assertEquals(PRIORITY_SENDERS_STARRED, mZenModeHelper.mConfig.allowCallsFrom);
- assertTrue(mZenModeHelper.mConfig.allowMessages);
- assertTrue(mZenModeHelper.mConfig.allowConversations);
- assertTrue(mZenModeHelper.mConfig.allowEvents);
- assertTrue(mZenModeHelper.mConfig.allowRepeatCallers);
- assertEquals(SUPPRESSED_EFFECT_BADGE, mZenModeHelper.mConfig.suppressedVisualEffects);
+ Policy customPolicy = new Policy(PRIORITY_CATEGORY_REMINDERS
+ | PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_MESSAGES
+ | PRIORITY_CATEGORY_EVENTS | PRIORITY_CATEGORY_REPEAT_CALLERS
+ | PRIORITY_CATEGORY_CONVERSATIONS,
+ PRIORITY_SENDERS_STARRED,
+ PRIORITY_SENDERS_STARRED,
+ SUPPRESSED_EFFECT_BADGE,
+ 0,
+ CONVERSATION_SENDERS_IMPORTANT);
+ mZenModeHelper.setNotificationPolicy(customPolicy, UPDATE_ORIGIN_UNKNOWN, 1);
+ if (!Flags.modesUi()) {
+ mZenModeHelper.mConfig.manualRule = null;
+ }
}
private void checkDndProtoMatchesSetupZenConfig(DNDPolicyProto dndProto) {
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
index 19ce217..9dac23f 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -1681,7 +1681,7 @@
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
.compose();
VibrationEffect effect4 = VibrationEffect.createOneShot(8000, 100);
- VibrationEffect effect5 = VibrationEffect.createOneShot(20, 222);
+ VibrationEffect effect5 = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
long vibrationId1 = startThreadAndDispatcher(effect1);
waitForCompletion();
@@ -1745,13 +1745,12 @@
verifyCallbacksTriggered(vibrationId4, Vibration.Status.CANCELLED_BY_SCREEN_OFF);
assertTrue("Tested duration=" + duration4, duration4 < 2000);
- // Effect5: normal oneshot. Don't worry about amplitude, as effect4 may or may not have
- // started.
+ // Effect5: played normally after effect4, which may or may not have played.
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId5));
verifyCallbacksTriggered(vibrationId5, Vibration.Status.FINISHED);
- assertEquals(Arrays.asList(expectedOneShot(20)),
+ assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
fakeVibrator.getEffectSegments(vibrationId5));
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 1fd8f50..ff1c6c8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -95,6 +95,8 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
import android.service.voice.IVoiceInteractionSession;
@@ -112,6 +114,7 @@
import com.android.server.wm.BackgroundActivityStartController.BalVerdict;
import com.android.server.wm.LaunchParamsController.LaunchParamsModifier;
import com.android.server.wm.utils.MockTracker;
+import com.android.wm.shell.Flags;
import org.junit.After;
import org.junit.Before;
@@ -492,7 +495,8 @@
// Start activity and delivered new intent.
starter.getIntent().setComponent(splitSecondReusableActivity.mActivityComponent);
- doReturn(splitSecondReusableActivity).when(mRootWindowContainer).findTask(any(), any());
+ doReturn(splitSecondReusableActivity)
+ .when(mRootWindowContainer).findTask(any(), any(), anyBoolean());
final int result = starter.setReason("testSplitScreenDeliverToTop").execute();
// Ensure result is delivering intent to top.
@@ -519,7 +523,8 @@
// Start activity and delivered new intent.
starter.getIntent().setComponent(splitSecondReusableActivity.mActivityComponent);
- doReturn(splitSecondReusableActivity).when(mRootWindowContainer).findTask(any(), any());
+ doReturn(splitSecondReusableActivity)
+ .when(mRootWindowContainer).findTask(any(), any(), anyBoolean());
final int result = starter.setReason("testSplitScreenMoveToFront").execute();
// Ensure result is moving task to front.
@@ -566,7 +571,7 @@
// Start activity and delivered new intent.
starter.getIntent().setComponent(activities.get(3).mActivityComponent);
- doReturn(activities.get(3)).when(mRootWindowContainer).findTask(any(), any());
+ doReturn(activities.get(3)).when(mRootWindowContainer).findTask(any(), any(), anyBoolean());
final int result = starter.setReason("testDesktopModeDeliverToTop").execute();
// Ensure result is delivering intent to top.
@@ -593,7 +598,8 @@
// Start activity and delivered new intent.
starter.getIntent().setComponent(desktopModeReusableActivity.mActivityComponent);
- doReturn(desktopModeReusableActivity).when(mRootWindowContainer).findTask(any(), any());
+ doReturn(desktopModeReusableActivity)
+ .when(mRootWindowContainer).findTask(any(), any(), anyBoolean());
final int result = starter.setReason("testDesktopModeMoveToFront").execute();
// Ensure result is moving task to front.
@@ -755,7 +761,7 @@
final ActivityRecord baseActivity = new ActivityBuilder(mAtm).setCreateTask(true).build();
baseActivity.getRootTask().setWindowingMode(WINDOWING_MODE_PINNED);
- doReturn(baseActivity).when(mRootWindowContainer).findTask(any(), any());
+ doReturn(baseActivity).when(mRootWindowContainer).findTask(any(), any(), anyBoolean());
ActivityOptions rawOptions = ActivityOptions.makeBasic()
.setPendingIntentCreatorBackgroundActivityStartMode(
@@ -1648,6 +1654,120 @@
assertNotEquals(inTask, target.getTask());
}
+ @EnableFlags(Flags.FLAG_ONLY_REUSE_BUBBLED_TASK_WHEN_LAUNCHED_FROM_BUBBLE)
+ @Test
+ public void launchActivity_reusesBubbledTask() {
+ final ActivityStarter starter = prepareStarter(0, false);
+ final ActivityRecord bubbledActivity = createBubbledActivity();
+
+ // create the target activity to be launched with the same component as the bubbled activity
+ final ActivityRecord targetRecord = new ActivityBuilder(mAtm)
+ .setLaunchMode(LAUNCH_SINGLE_TASK)
+ .setComponent(ActivityBuilder.getDefaultComponent()).build();
+ starter.getIntent().setComponent(bubbledActivity.mActivityComponent);
+ startActivityInner(starter, targetRecord, bubbledActivity, null /* options */,
+ null /* inTask */, null /* inTaskFragment */);
+
+ assertEquals(bubbledActivity.getTask(), targetRecord.getTask());
+ }
+
+ @EnableFlags(Flags.FLAG_ONLY_REUSE_BUBBLED_TASK_WHEN_LAUNCHED_FROM_BUBBLE)
+ @Test
+ public void launchActivity_nullSourceRecord_doesNotReuseBubbledTask() {
+ final ActivityStarter starter = prepareStarter(0, false);
+ final ActivityRecord bubbledActivity = createBubbledActivity();
+
+ // create the target activity to be launched
+ final ActivityRecord targetRecord =
+ new ActivityBuilder(mAtm)
+ .setLaunchMode(LAUNCH_SINGLE_TASK)
+ .setComponent(ActivityBuilder.getDefaultComponent()).build();
+ starter.getIntent().setComponent(bubbledActivity.mActivityComponent);
+
+ // pass null as the source record
+ startActivityInner(starter, targetRecord, null, null /* options */,
+ null /* inTask */, null /* inTaskFragment */);
+
+ assertNotEquals(bubbledActivity.getTask(), targetRecord.getTask());
+ }
+
+ @EnableFlags(Flags.FLAG_ONLY_REUSE_BUBBLED_TASK_WHEN_LAUNCHED_FROM_BUBBLE)
+ @Test
+ public void launchActivity_nonBubbledSourceRecord_doesNotReuseBubbledTask() {
+ final ActivityStarter starter = prepareStarter(0, false);
+ final ActivityRecord bubbledActivity = createBubbledActivity();
+
+ // create a non bubbled activity
+ final ActivityRecord nonBubbleSourceRecord =
+ new ActivityBuilder(mAtm).setCreateTask(true)
+ .setLaunchMode(LAUNCH_SINGLE_TASK)
+ .setComponent(ActivityBuilder.getDefaultComponent())
+ .build();
+
+ // create the target activity to be launched
+ final ActivityRecord targetRecord =
+ new ActivityBuilder(mAtm)
+ .setLaunchMode(LAUNCH_SINGLE_TASK)
+ .setComponent(ActivityBuilder.getDefaultComponent()).build();
+ starter.getIntent().setComponent(bubbledActivity.mActivityComponent);
+
+ // use the non bubbled activity as the source
+ startActivityInner(starter, targetRecord, nonBubbleSourceRecord, null /* options */,
+ null /* inTask */, null /* inTaskFragment*/);
+
+ assertNotEquals(bubbledActivity.getTask(), targetRecord.getTask());
+ }
+
+ @DisableFlags(Flags.FLAG_ONLY_REUSE_BUBBLED_TASK_WHEN_LAUNCHED_FROM_BUBBLE)
+ @Test
+ public void launchActivity_nullSourceRecord_flagDisabled_reusesBubbledTask() {
+ final ActivityStarter starter = prepareStarter(0, false);
+ final ActivityRecord bubbledActivity = createBubbledActivity();
+
+ // create the target activity to be launched
+ final ActivityRecord targetRecord =
+ new ActivityBuilder(mAtm)
+ .setLaunchMode(LAUNCH_SINGLE_TASK)
+ .setComponent(ActivityBuilder.getDefaultComponent()).build();
+ starter.getIntent().setComponent(bubbledActivity.mActivityComponent);
+
+ // pass null as the source record
+ startActivityInner(starter, targetRecord, null, null /* options */,
+ null /* inTask */, null /* inTaskFragment */);
+
+ assertEquals(bubbledActivity.getTask(), targetRecord.getTask());
+ }
+
+ @DisableFlags(Flags.FLAG_ONLY_REUSE_BUBBLED_TASK_WHEN_LAUNCHED_FROM_BUBBLE)
+ @Test
+ public void launchActivity_fromBubble_flagDisabled_reusesBubbledTask() {
+ final ActivityStarter starter = prepareStarter(0, false);
+ final ActivityRecord bubbledActivity = createBubbledActivity();
+
+ // create the target activity to be launched with the same component as the bubbled activity
+ final ActivityRecord targetRecord =
+ new ActivityBuilder(mAtm)
+ .setLaunchMode(LAUNCH_SINGLE_TASK)
+ .setComponent(ActivityBuilder.getDefaultComponent()).build();
+ starter.getIntent().setComponent(bubbledActivity.mActivityComponent);
+ startActivityInner(starter, targetRecord, bubbledActivity, null /* options */,
+ null /* inTask */, null /* inTaskFragment */);
+
+ assertEquals(bubbledActivity.getTask(), targetRecord.getTask());
+ }
+
+ private ActivityRecord createBubbledActivity() {
+ final ActivityOptions opts = ActivityOptions.makeBasic();
+ opts.setTaskAlwaysOnTop(true);
+ opts.setLaunchedFromBubble(true);
+ opts.setLaunchBounds(new Rect(10, 10, 100, 100));
+ return new ActivityBuilder(mAtm)
+ .setCreateTask(true)
+ .setComponent(ActivityBuilder.getDefaultComponent())
+ .setActivityOptions(opts)
+ .build();
+ }
+
private static void startActivityInner(ActivityStarter starter, ActivityRecord target,
ActivityRecord source, ActivityOptions options, Task inTask,
TaskFragment inTaskFragment) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index c67d1ec..a39a1a8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -550,7 +550,7 @@
}).when(appWindow.mSession).setOnBackInvokedCallbackInfo(eq(appWindow.mClient), any());
addToWindowMap(appWindow, true);
- dispatcher.attachToWindow(appWindow.mSession, appWindow.mClient, null, null);
+ dispatcher.attachToWindow(appWindow.mSession, appWindow.mClient, null);
OnBackInvokedCallback appCallback = createBackCallback(appLatch);
diff --git a/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java b/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java
index 8cf2776..3e87f1f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java
@@ -125,7 +125,7 @@
processDuration < PRE_TASK_DELAY_MS);
assertTrue("Target didn't call callback enough times.",
- mFactory.waitForAllExpectedItemsProcessed(TIMEOUT_ALLOWANCE));
+ mListener.waitForAllExpectedCallbackDone(TIMEOUT_ALLOWANCE));
// Once before processing this item, once after that.
assertEquals(2, mListener.mProbablyDoneResults.size());
// The last one must be called with probably done being true.
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
index ce90504..e019a41 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
@@ -405,11 +405,12 @@
final RootWindowContainer.FindTaskResult result =
new RootWindowContainer.FindTaskResult();
- result.init(r.getActivityType(), r.taskAffinity, r.intent, r.info);
+ result.init(r.getActivityType(), r.taskAffinity, r.intent, r.info, true);
result.process(task);
- assertEquals(r, task.getTopNonFinishingActivity(false /* includeOverlays */));
- assertEquals(taskOverlay, task.getTopNonFinishingActivity(true /* includeOverlays */));
+ assertEquals(r, task.getTopNonFinishingActivity(false /* includeOverlays */, true));
+ assertEquals(
+ taskOverlay, task.getTopNonFinishingActivity(true /* includeOverlays */, true));
assertNotNull(result.mIdealRecord);
}
@@ -432,7 +433,7 @@
final ActivityRecord r1 = new ActivityBuilder(mAtm).setComponent(
target).setTargetActivity(targetActivity).build();
RootWindowContainer.FindTaskResult result = new RootWindowContainer.FindTaskResult();
- result.init(r1.getActivityType(), r1.taskAffinity, r1.intent, r1.info);
+ result.init(r1.getActivityType(), r1.taskAffinity, r1.intent, r1.info, true);
result.process(parentTask);
assertThat(result.mIdealRecord).isNotNull();
@@ -440,7 +441,7 @@
final ActivityRecord r2 = new ActivityBuilder(mAtm).setComponent(
alias).setTargetActivity(targetActivity).build();
result = new RootWindowContainer.FindTaskResult();
- result.init(r2.getActivityType(), r2.taskAffinity, r2.intent, r2.info);
+ result.init(r2.getActivityType(), r2.taskAffinity, r2.intent, r2.info, true);
result.process(parentTask);
assertThat(result.mIdealRecord).isNotNull();
}
@@ -1234,12 +1235,18 @@
assertEquals(STOPPING, activity2.getState());
assertThat(mSupervisor.mStoppingActivities).contains(activity2);
+ registerTestTransitionPlayer();
+ final Transition transition = display.mTransitionController
+ .requestCloseTransitionIfNeeded(rootTask1);
+ transition.collectClose(rootTask1);
// The display becomes empty. Since there is no next activity to be idle, the activity
// should be destroyed immediately with updating configuration to restore original state.
final ActivityRecord activity1 = finishTopActivity(rootTask1);
assertEquals(DESTROYING, activity1.getState());
verify(mRootWindowContainer).ensureVisibilityAndConfig(eq(null) /* starting */,
eq(display), anyBoolean());
+ assertTrue("Transition must be ready if there is no next running activity",
+ transition.allReady());
}
private ActivityRecord finishTopActivity(Task task) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index d88871c..eb79118 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -147,6 +147,40 @@
}
@Test
+ public void testFindTask_includeLaunchedFromBubbled() {
+ final ComponentName component = ComponentName.createRelative(
+ DEFAULT_COMPONENT_PACKAGE_NAME, ".BubbledActivity");
+ final ActivityOptions opts = ActivityOptions.makeBasic();
+ opts.setTaskAlwaysOnTop(true);
+ opts.setLaunchedFromBubble(true);
+ final ActivityRecord activity = new ActivityBuilder(mWm.mAtmService)
+ .setComponent(component)
+ .setActivityOptions(opts)
+ .setCreateTask(true)
+ .build();
+
+ assertEquals(activity, mWm.mRoot.findTask(activity, activity.getTaskDisplayArea(),
+ true /* includeLaunchedFromBubble */));
+ }
+
+ @Test
+ public void testFindTask_ignoreLaunchedFromBubbled() {
+ final ComponentName component = ComponentName.createRelative(
+ DEFAULT_COMPONENT_PACKAGE_NAME, ".BubbledActivity");
+ final ActivityOptions opts = ActivityOptions.makeBasic();
+ opts.setTaskAlwaysOnTop(true);
+ opts.setLaunchedFromBubble(true);
+ final ActivityRecord activity = new ActivityBuilder(mWm.mAtmService)
+ .setComponent(component)
+ .setActivityOptions(opts)
+ .setCreateTask(true)
+ .build();
+
+ assertNull(mWm.mRoot.findTask(activity, activity.getTaskDisplayArea(),
+ false /* includeLaunchedFromBubble */));
+ }
+
+ @Test
public void testAllPausedActivitiesComplete() {
DisplayContent displayContent = mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY);
ActivityRecord activity = createActivityRecord(displayContent);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 7adac5b..1a366b3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -105,7 +105,6 @@
import android.os.UserHandle;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsDisabled;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;
import android.view.InsetsFrameProvider;
@@ -339,10 +338,9 @@
}
}
- // TODO(b/333663877): Enable test after fix
@Test
- @RequiresFlagsDisabled({Flags.FLAG_INSETS_DECOUPLED_CONFIGURATION})
@EnableFlags(Flags.FLAG_IMMERSIVE_APP_REPOSITIONING)
+ @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED})
public void testRepositionLandscapeImmersiveAppWithDisplayCutout() {
final int dw = 2100;
final int dh = 2000;
@@ -356,11 +354,14 @@
mWm.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(0.5f);
mWm.mLetterboxConfiguration.setIsVerticalReachabilityEnabled(true);
- doReturn(true).when(mActivity).isImmersiveMode(any());
- prepareMinAspectRatio(mActivity, OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
- SCREEN_ORIENTATION_LANDSCAPE);
- addWindowToActivity(mActivity);
- mActivity.mRootWindowContainer.performSurfacePlacement();
+ final ActivityRecord activity = getActivityBuilderOnSameTask()
+ .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
+ .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE)
+ .build();
+
+ doReturn(true).when(activity).isImmersiveMode(any());
+ addWindowToActivity(activity);
+ activity.mRootWindowContainer.performSurfacePlacement();
final Function<ActivityRecord, Rect> innerBoundsOf =
(ActivityRecord a) -> {
@@ -371,22 +372,22 @@
final Consumer<Integer> doubleClick =
(Integer y) -> {
- mActivity.mLetterboxUiController.handleVerticalDoubleTap(y);
- mActivity.mRootWindowContainer.performSurfacePlacement();
+ activity.mLetterboxUiController.handleVerticalDoubleTap(y);
+ activity.mRootWindowContainer.performSurfacePlacement();
};
- final Rect bounds = mActivity.getBounds();
+ final Rect bounds = activity.getBounds();
assertTrue(bounds.top > cutoutHeight && bounds.bottom < dh);
assertEquals(dw, bounds.width());
// Double click bottom.
doubleClick.accept(dh - 10);
- assertEquals(dh, innerBoundsOf.apply(mActivity).bottom);
+ assertEquals(dh, innerBoundsOf.apply(activity).bottom);
// Double click top.
doubleClick.accept(10);
doubleClick.accept(10);
- assertEquals(cutoutHeight, innerBoundsOf.apply(mActivity).top);
+ assertEquals(cutoutHeight, innerBoundsOf.apply(activity).top);
}
@Test
@@ -417,26 +418,25 @@
}
@Test
+ @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED})
public void testFixedAspectRatioBoundsWithDecorInSquareDisplay() {
- if (Flags.insetsDecoupledConfiguration()) {
- // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config
- // bounds no longer contains display cutout.
- return;
- }
final int notchHeight = 100;
setUpApp(new TestDisplayContent.Builder(mAtm, 600, 800).setNotch(notchHeight).build());
final Rect displayBounds = mActivity.mDisplayContent.getWindowConfiguration().getBounds();
final float aspectRatio = 1.2f;
- mActivity.info.setMaxAspectRatio(aspectRatio);
- mActivity.info.setMinAspectRatio(aspectRatio);
- prepareUnresizable(mActivity, -1f, SCREEN_ORIENTATION_UNSPECIFIED);
- final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds();
+ final ActivityRecord activity = getActivityBuilderOnSameTask()
+ .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED)
+ .setMinAspectRatio(aspectRatio)
+ .setMaxAspectRatio(aspectRatio)
+ .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
+ .build();
+ final Rect appBounds = activity.getWindowConfiguration().getAppBounds();
// The parent configuration doesn't change since the first resolved configuration, so the
// activity should fit in the parent naturally (size=583x700, appBounds=[9, 100 - 592, 800],
// horizontal offset = round((600 - 583) / 2) = 9)).
- assertFitted();
+ assertFitted(activity);
final int offsetX = (int) ((1f + displayBounds.width() - appBounds.width()) / 2);
// The bounds must be horizontal centered.
assertEquals(offsetX, appBounds.left);
@@ -444,30 +444,30 @@
// Ensure the app bounds keep the declared aspect ratio.
assertEquals(appBounds.height(), appBounds.width() * aspectRatio, 0.5f /* delta */);
// The decor height should be a part of the effective bounds.
- assertEquals(mActivity.getBounds().height(), appBounds.height() + notchHeight);
+ assertEquals(activity.getBounds().height(), appBounds.height() + notchHeight);
// Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio.
- assertActivityMaxBoundsSandboxed();
+ assertActivityMaxBoundsSandboxed(activity);
// Activity max bounds ignore notch, since an app can be shown past the notch (although app
// is currently limited by the notch).
- assertThat(mActivity.getWindowConfiguration().getMaxBounds().height())
+ assertThat(activity.getWindowConfiguration().getMaxBounds().height())
.isEqualTo(displayBounds.height());
- mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
- assertFitted();
+ activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ assertFitted(activity);
// After the orientation of activity is changed, the display is rotated, the aspect
// ratio should be the same (bounds=[0, 0 - 800, 583], appBounds=[100, 0 - 800, 583]).
assertEquals(appBounds.width(), appBounds.height() * aspectRatio, 0.5f /* delta */);
// Activity max bounds are sandboxed.
- assertActivityMaxBoundsSandboxed();
+ assertActivityMaxBoundsSandboxed(activity);
- mActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
- assertFitted();
+ activity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
+ assertFitted(activity);
// Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio.
- assertActivityMaxBoundsSandboxed();
+ assertActivityMaxBoundsSandboxed(activity);
// Activity max bounds ignore notch, since an app can be shown past the notch (although app
// is currently limited by the notch).
- assertThat(mActivity.getWindowConfiguration().getMaxBounds().height())
+ assertThat(activity.getWindowConfiguration().getMaxBounds().height())
.isEqualTo(displayBounds.height());
}
@@ -674,12 +674,8 @@
@Test
public void testMoveToDifferentOrientationDisplay() {
- if (Flags.insetsDecoupledConfiguration()) {
- // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config
- // bounds no longer contains display cutout.
- return;
- }
setUpDisplaySizeWithApp(1000, 2500);
+ mActivity.mResolveConfigHint.mUseOverrideInsetsForConfig = true;
prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
assertFitted();
@@ -726,16 +722,12 @@
@Test
public void testFixedOrientationRotateCutoutDisplay() {
- if (Flags.insetsDecoupledConfiguration()) {
- // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config
- // bounds no longer contains display cutout.
- return;
- }
// Create a display with a notch/cutout
final int notchHeight = 60;
final int width = 1000;
setUpApp(new TestDisplayContent.Builder(mAtm, width, 2500)
.setNotch(notchHeight).build());
+ mActivity.mResolveConfigHint.mUseOverrideInsetsForConfig = true;
// Bounds=[0, 0 - 1000, 1400], AppBounds=[0, 60 - 1000, 1460].
final float maxAspect = 1.4f;
prepareUnresizable(mActivity, 1.4f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
@@ -1328,12 +1320,8 @@
setUpDisplaySizeWithApp(1000, 1200);
// Create a size compat activity on the same task.
- final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setTask(mTask)
+ final ActivityRecord activity = getActivityBuilderOnSameTask()
.setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
- .setComponent(ComponentName.createRelative(mContext,
- SizeCompatTests.class.getName()))
- .setUid(android.os.Process.myUid())
.build();
// The per-package override forces the activity into a 3:2 aspect ratio
@@ -1345,24 +1333,18 @@
@Test
@EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
+ @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED})
public void testOverrideMinAspectRatioLowerThanManifest() {
- if (Flags.insetsDecoupledConfiguration()) {
- // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config
- // bounds no longer contains display cutout.
- return;
- }
- final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1400, 1800)
- .setNotch(200).setSystemDecorations(true).build();
+ final int dh = 1800;
+ final int notchHeight = 200;
+ final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1400, dh)
+ .setNotch(notchHeight).setSystemDecorations(true).build();
mTask = new TaskBuilder(mSupervisor).setDisplay(display).build();
// Create a size compat activity on the same task.
- final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setTask(mTask)
+ final ActivityRecord activity = getActivityBuilderOnSameTask()
.setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
- .setComponent(ComponentName.createRelative(mContext,
- SizeCompatTests.class.getName()))
.setMinAspectRatio(2f)
- .setUid(android.os.Process.myUid())
.build();
// The per-package override should have no effect, because the manifest aspect ratio is
@@ -1371,7 +1353,7 @@
assertEquals("App bounds must have min aspect ratio", 2f,
(float) appBounds.height() / appBounds.width(), 0.0001f /* delta */);
assertEquals("Long side must fit task",
- mTask.getWindowConfiguration().getAppBounds().height(), appBounds.height());
+ dh - notchHeight, appBounds.height());
assertEquals("Bounds can include insets", mTask.getBounds().height(),
activity.getBounds().height());
}
@@ -1383,13 +1365,9 @@
setUpDisplaySizeWithApp(1400, 1600);
// Create a size compat activity on the same task.
- final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setTask(mTask)
+ final ActivityRecord activity = getActivityBuilderOnSameTask()
.setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
- .setComponent(ComponentName.createRelative(mContext,
- SizeCompatTests.class.getName()))
.setMinAspectRatio(1.1f)
- .setUid(android.os.Process.myUid())
.build();
// The per-package override should have no effect, because the manifest aspect ratio is
@@ -1406,12 +1384,8 @@
setUpDisplaySizeWithApp(1500, 1600);
// Create a size compat activity on the same task.
- final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setTask(mTask)
+ final ActivityRecord activity = getActivityBuilderOnSameTask()
.setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
- .setComponent(ComponentName.createRelative(mContext,
- SizeCompatTests.class.getName()))
- .setUid(android.os.Process.myUid())
.build();
// The per-package override forces the activity into a 16:9 aspect ratio
@@ -1430,12 +1404,8 @@
setUpDisplaySizeWithApp(1400, 1600);
// Create a size compat activity on the same task.
- final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setTask(mTask)
+ final ActivityRecord activity = getActivityBuilderOnSameTask()
.setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
- .setComponent(ComponentName.createRelative(mContext,
- SizeCompatTests.class.getName()))
- .setUid(android.os.Process.myUid())
.build();
// The per-package override forces the activity into a 16:9 aspect ratio
@@ -1455,12 +1425,7 @@
setUpDisplaySizeWithApp(1000, 1200);
// Create a size compat activity on the same task.
- final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setTask(mTask)
- .setComponent(ComponentName.createRelative(mContext,
- SizeCompatTests.class.getName()))
- .setUid(android.os.Process.myUid())
- .build();
+ final ActivityRecord activity = getActivityBuilderOnSameTask().build();
// The per-package override should have no effect
assertEquals(1200, activity.getBounds().height());
@@ -1487,12 +1452,8 @@
setUpDisplaySizeWithApp(1000, 1200);
// Create a size compat activity on the same task.
- final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setTask(mTask)
+ final ActivityRecord activity = getActivityBuilderOnSameTask()
.setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
- .setComponent(ComponentName.createRelative(mContext,
- SizeCompatTests.class.getName()))
- .setUid(android.os.Process.myUid())
.build();
// The per-package override should have no effect
@@ -1517,12 +1478,8 @@
setUpDisplaySizeWithApp(1000, 1200);
// Create a size compat activity on the same task.
- final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setTask(mTask)
+ final ActivityRecord activity = getActivityBuilderOnSameTask()
.setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
- .setComponent(ComponentName.createRelative(mContext,
- SizeCompatTests.class.getName()))
- .setUid(android.os.Process.myUid())
.build();
// The per-package override forces the activity into a 3:2 aspect ratio
@@ -1547,12 +1504,7 @@
setUpDisplaySizeWithApp(1000, 1200);
// Create a size compat activity on the same task.
- final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setTask(mTask)
- .setComponent(ComponentName.createRelative(mContext,
- SizeCompatTests.class.getName()))
- .setUid(android.os.Process.myUid())
- .build();
+ final ActivityRecord activity = getActivityBuilderOnSameTask().build();
// The per-package override forces the activity into a 3:2 aspect ratio
assertEquals(1200, activity.getBounds().height());
@@ -1571,12 +1523,8 @@
setUpDisplaySizeWithApp(1000, 1200);
// Create a size compat activity on the same task.
- final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setTask(mTask)
+ final ActivityRecord activity = getActivityBuilderOnSameTask()
.setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
- .setComponent(ComponentName.createRelative(mContext,
- SizeCompatTests.class.getName()))
- .setUid(android.os.Process.myUid())
.build();
// The per-package override forces the activity into a 3:2 aspect ratio
@@ -1594,12 +1542,8 @@
setUpDisplaySizeWithApp(1000, 1200);
// Create a size compat activity on the same task.
- final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setTask(mTask)
+ final ActivityRecord activity = getActivityBuilderOnSameTask()
.setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
- .setComponent(ComponentName.createRelative(mContext,
- SizeCompatTests.class.getName()))
- .setUid(android.os.Process.myUid())
.build();
// The per-package override should have no effect
@@ -1614,12 +1558,8 @@
setUpDisplaySizeWithApp(/* dw= */ 1000, /* dh= */ 2800);
// Create a size compat activity on the same task.
- final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setTask(mTask)
+ final ActivityRecord activity = getActivityBuilderOnSameTask()
.setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
- .setComponent(ComponentName.createRelative(mContext,
- SizeCompatTests.class.getName()))
- .setUid(android.os.Process.myUid())
.build();
final TestSplitOrganizer organizer =
@@ -1697,15 +1637,11 @@
@Test
public void testLaunchWithFixedRotationTransform() {
- if (Flags.insetsDecoupledConfiguration()) {
- // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config
- // bounds no longer contains display cutout.
- return;
- }
final int dw = 1000;
final int dh = 2500;
final int notchHeight = 200;
setUpApp(new TestDisplayContent.Builder(mAtm, dw, dh).setNotch(notchHeight).build());
+ mActivity.mResolveConfigHint.mUseOverrideInsetsForConfig = true;
// The test assumes the notch will be at left side when the orientation is landscape.
if (mContext.getResources().getBoolean(
com.android.internal.R.bool.config_reverseDefaultRotation)) {
@@ -2315,12 +2251,7 @@
private void testUserOverrideAspectRatio(boolean isUnresizable, int screenOrientation,
float expectedAspectRatio, @PackageManager.UserMinAspectRatio int aspectRatio,
boolean enabled) {
- final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setTask(mTask)
- .setComponent(ComponentName.createRelative(mContext,
- SizeCompatTests.class.getName()))
- .setUid(android.os.Process.myUid())
- .build();
+ final ActivityRecord activity = getActivityBuilderOnSameTask().build();
activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
spyOn(activity.mWmService.mLetterboxConfiguration);
doReturn(enabled).when(activity.mWmService.mLetterboxConfiguration)
@@ -2351,12 +2282,8 @@
final int displayWidth = 1400;
final int displayHeight = 1600;
setUpDisplaySizeWithApp(displayWidth, displayHeight);
- final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setTask(mTask)
- .setComponent(ComponentName.createRelative(mContext,
- SizeCompatTests.class.getName()))
+ final ActivityRecord activity = getActivityBuilderOnSameTask()
.setMinAspectRatio(1.1f)
- .setUid(android.os.Process.myUid())
.build();
// Setup Letterbox Configuration
activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
@@ -2376,12 +2303,8 @@
final int displayWidth = 1600;
final int displayHeight = 1400;
setUpDisplaySizeWithApp(displayWidth, displayHeight);
- final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setTask(mTask)
- .setComponent(ComponentName.createRelative(mContext,
- SizeCompatTests.class.getName()))
+ final ActivityRecord activity = getActivityBuilderOnSameTask()
.setMinAspectRatio(1.1f)
- .setUid(android.os.Process.myUid())
.build();
// Setup Letterbox Configuration
activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
@@ -2402,12 +2325,8 @@
final int displayWidth = 1400;
final int displayHeight = 1600;
setUpDisplaySizeWithApp(displayWidth, displayHeight);
- final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setTask(mTask)
- .setComponent(ComponentName.createRelative(mContext,
- SizeCompatTests.class.getName()))
+ final ActivityRecord activity = getActivityBuilderOnSameTask()
.setMinAspectRatio(1.1f)
- .setUid(android.os.Process.myUid())
.build();
// Setup Letterbox Configuration
activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
@@ -2428,12 +2347,8 @@
final int displayWidth = 1600;
final int displayHeight = 1400;
setUpDisplaySizeWithApp(displayWidth, displayHeight);
- final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setTask(mTask)
- .setComponent(ComponentName.createRelative(mContext,
- SizeCompatTests.class.getName()))
+ final ActivityRecord activity = getActivityBuilderOnSameTask()
.setMinAspectRatio(1.1f)
- .setUid(android.os.Process.myUid())
.build();
// Setup Letterbox Configuration
activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
@@ -2454,12 +2369,7 @@
final int screenWidth = 1800;
final int screenHeight = 1000;
setUpDisplaySizeWithApp(screenWidth, screenHeight);
- final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setTask(mTask)
- .setComponent(ComponentName.createRelative(mContext,
- SizeCompatTests.class.getName()))
- .setUid(android.os.Process.myUid())
- .build();
+ final ActivityRecord activity = getActivityBuilderOnSameTask().build();
activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
// Simulate real display with top insets.
@@ -2495,12 +2405,7 @@
final int screenWidth = 1000;
final int screenHeight = 1800;
setUpDisplaySizeWithApp(screenWidth, screenHeight);
- final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setTask(mTask)
- .setComponent(ComponentName.createRelative(mContext,
- SizeCompatTests.class.getName()))
- .setUid(android.os.Process.myUid())
- .build();
+ final ActivityRecord activity = getActivityBuilderOnSameTask().build();
activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
// Simulate real display with top insets.
@@ -2538,12 +2443,7 @@
mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.33f);
// Create a size compat activity on the same task.
- final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setTask(mTask)
- .setComponent(ComponentName.createRelative(mContext,
- SizeCompatTests.class.getName()))
- .setUid(android.os.Process.myUid())
- .build();
+ final ActivityRecord activity = getActivityBuilderOnSameTask().build();
// Non-resizable portrait activity
prepareUnresizable(activity, 0f, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
@@ -2573,12 +2473,7 @@
mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.33f);
// Create a size compat activity on the same task.
- final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setTask(mTask)
- .setComponent(ComponentName.createRelative(mContext,
- SizeCompatTests.class.getName()))
- .setUid(android.os.Process.myUid())
- .build();
+ final ActivityRecord activity = getActivityBuilderOnSameTask().build();
final TestSplitOrganizer organizer =
new TestSplitOrganizer(mAtm, activity.getDisplayContent());
@@ -3636,17 +3531,13 @@
@Test
public void testLetterboxDetailsForStatusBar_letterboxNotOverlappingStatusBar() {
- if (Flags.insetsDecoupledConfiguration()) {
- // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config
- // bounds no longer contains display cutout.
- return;
- }
// Align to center so that we don't overlap with the status bar
mAtm.mWindowManager.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(0.5f);
final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2800)
.setNotch(100)
.build();
setUpApp(display);
+ mActivity.mResolveConfigHint.mUseOverrideInsetsForConfig = true;
TestWindowState statusBar = addStatusBar(mActivity.mDisplayContent);
spyOn(statusBar);
doReturn(new Rect(0, 0, statusBar.mRequestedWidth, statusBar.mRequestedHeight))
@@ -3658,12 +3549,12 @@
// Refresh the letterbox
mActivity.mRootWindowContainer.performSurfacePlacement();
- Rect mBounds = new Rect(mActivity.getWindowConfiguration().getBounds());
- assertEquals(mBounds, new Rect(0, 900, 1000, 2000));
+ Rect bounds = new Rect(mActivity.getWindowConfiguration().getBounds());
+ assertEquals(new Rect(0, 900, 1000, 2000), bounds);
DisplayPolicy displayPolicy = mActivity.getDisplayContent().getDisplayPolicy();
LetterboxDetails[] expectedLetterboxDetails = {new LetterboxDetails(
- mBounds,
+ bounds,
mActivity.getDisplayContent().getBounds(),
mActivity.findMainWindow().mAttrs.insetsFlags.appearance
)};
@@ -3952,12 +3843,8 @@
assertTrue(display.getDisplayPolicy().updateDecorInsetsInfo());
display.sendNewConfiguration();
- final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setTask(mTask)
+ final ActivityRecord activity = getActivityBuilderOnSameTask()
.setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
- .setComponent(ComponentName.createRelative(mContext,
- SizeCompatTests.class.getName()))
- .setUid(android.os.Process.myUid())
.build();
// Activity should not be letterboxed and should have portrait app bounds even though
@@ -3989,12 +3876,8 @@
assertTrue(display.getDisplayPolicy().updateDecorInsetsInfo());
display.sendNewConfiguration();
- final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setTask(mTask)
+ final ActivityRecord activity = getActivityBuilderOnSameTask()
.setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
- .setComponent(ComponentName.createRelative(mContext,
- SizeCompatTests.class.getName()))
- .setUid(android.os.Process.myUid())
.build();
final Rect bounds = activity.getBounds();
@@ -4021,14 +3904,11 @@
assertTrue(dc.getDisplayPolicy().updateDecorInsetsInfo());
dc.sendNewConfiguration();
- final ActivityRecord activity = new ActivityBuilder(mAtm)
- .setTask(mTask)
- .setComponent(ComponentName.createRelative(mContext,
- SizeCompatTests.class.getName()))
- .setUid(android.os.Process.myUid())
+ final ActivityRecord activity = getActivityBuilderOnSameTask()
+ .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
+ .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
+ .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE)
.build();
- prepareMinAspectRatio(activity, OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
- SCREEN_ORIENTATION_LANDSCAPE);
// To force config to update again but with the same landscape orientation.
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
@@ -4042,11 +3922,6 @@
@Test
public void testApplyAspectRatio_activityAlignWithParentAppVertical() {
- if (Flags.insetsDecoupledConfiguration()) {
- // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config
- // bounds no longer contains display cutout.
- return;
- }
// The display's app bounds will be (0, 100, 1000, 2350)
final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2500)
.setCanRotate(false)
@@ -4054,19 +3929,16 @@
.build();
setUpApp(display);
- prepareUnresizable(mActivity, 2.1f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
+ mActivity.mResolveConfigHint.mUseOverrideInsetsForConfig = true;
+ prepareUnresizable(mActivity, 2.1f, SCREEN_ORIENTATION_UNSPECIFIED);
// The activity height is 2100 and the display's app bounds height is 2250, so the activity
// can be aligned inside parentAppBounds
- assertEquals(mActivity.getBounds(), new Rect(0, 0, 1000, 2200));
+ assertEquals(new Rect(0, 0, 1000, 2200), mActivity.getBounds());
}
@Test
+ @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED})
public void testApplyAspectRatio_activityCannotAlignWithParentAppVertical() {
- if (Flags.insetsDecoupledConfiguration()) {
- // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config
- // bounds no longer contains display cutout.
- return;
- }
// The display's app bounds will be (0, 100, 1000, 2150)
final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2300)
.setCanRotate(false)
@@ -4074,19 +3946,21 @@
.build();
setUpApp(display);
- prepareUnresizable(mActivity, 2.1f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
+
+ final ActivityRecord activity = getActivityBuilderOnSameTask()
+ .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
+ .setMaxAspectRatio(2.1f)
+ .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED)
+ .build();
+
// The activity height is 2100 and the display's app bounds height is 2050, so the activity
// cannot be aligned inside parentAppBounds and it will fill the parentBounds of the display
- assertEquals(mActivity.getBounds(), display.getBounds());
+ assertEquals(activity.getBounds(), display.getBounds());
}
@Test
+ @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED})
public void testApplyAspectRatio_activityAlignWithParentAppHorizontal() {
- if (Flags.insetsDecoupledConfiguration()) {
- // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config
- // bounds no longer contains display cutout.
- return;
- }
// The display's app bounds will be (100, 0, 2350, 1000)
final DisplayContent display = new TestDisplayContent.Builder(mAtm, 2500, 1000)
.setCanRotate(false)
@@ -4094,18 +3968,19 @@
.build();
setUpApp(display);
- prepareUnresizable(mActivity, 2.1f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
+
+ final ActivityRecord activity = getActivityBuilderOnSameTask()
+ .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
+ .setMaxAspectRatio(2.1f)
+ .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED)
+ .build();
// The activity width is 2100 and the display's app bounds width is 2250, so the activity
// can be aligned inside parentAppBounds
- assertEquals(mActivity.getBounds(), new Rect(175, 0, 2275, 1000));
+ assertEquals(activity.getBounds(), new Rect(175, 0, 2275, 1000));
}
@Test
+ @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED})
public void testApplyAspectRatio_activityCannotAlignWithParentAppHorizontal() {
- if (Flags.insetsDecoupledConfiguration()) {
- // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config
- // bounds no longer contains display cutout.
- return;
- }
// The display's app bounds will be (100, 0, 2150, 1000)
final DisplayContent display = new TestDisplayContent.Builder(mAtm, 2300, 1000)
.setCanRotate(false)
@@ -4113,10 +3988,14 @@
.build();
setUpApp(display);
- prepareUnresizable(mActivity, 2.1f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
+ final ActivityRecord activity = getActivityBuilderOnSameTask()
+ .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
+ .setMaxAspectRatio(2.1f)
+ .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED)
+ .build();
// The activity width is 2100 and the display's app bounds width is 2050, so the activity
// cannot be aligned inside parentAppBounds and it will fill the parentBounds of the display
- assertEquals(mActivity.getBounds(), display.getBounds());
+ assertEquals(activity.getBounds(), display.getBounds());
}
@Test
@@ -4344,26 +4223,23 @@
@Test
public void testUpdateResolvedBoundsPosition_alignToTop() {
- if (Flags.insetsDecoupledConfiguration()) {
- // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config
- // bounds no longer contains display cutout.
- return;
- }
final int notchHeight = 100;
final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2800)
.setNotch(notchHeight)
.build();
setUpApp(display);
+ mActivity.mResolveConfigHint.mUseOverrideInsetsForConfig = true;
// Prepare unresizable activity with max aspect ratio
- prepareUnresizable(mActivity, /* maxAspect */ 1.1f, SCREEN_ORIENTATION_UNSPECIFIED);
+ prepareUnresizable(mActivity, 1.1f, SCREEN_ORIENTATION_UNSPECIFIED);
- Rect mBounds = new Rect(mActivity.getWindowConfiguration().getBounds());
+ Rect bounds = new Rect(mActivity.getWindowConfiguration().getBounds());
Rect appBounds = new Rect(mActivity.getWindowConfiguration().getAppBounds());
// The insets should be cut for aspect ratio and then added back because the appBounds
// are aligned to the top of the parentAppBounds
- assertEquals(mBounds, new Rect(0, 0, 1000, 1200));
- assertEquals(appBounds, new Rect(0, notchHeight, 1000, 1200));
+ assertEquals(new Rect(0, notchHeight, 1000, 1200), appBounds);
+ assertEquals(new Rect(0, 0, 1000, 1200), bounds);
+
}
private void assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity(
@@ -4859,15 +4735,19 @@
*/
private ActivityRecord buildActivityRecord(boolean supportsSizeChanges, int resizeMode,
@ScreenOrientation int screenOrientation) {
- return new ActivityBuilder(mAtm)
- .setTask(mTask)
+ return getActivityBuilderOnSameTask()
.setResizeMode(resizeMode)
.setSupportsSizeChanges(supportsSizeChanges)
.setScreenOrientation(screenOrientation)
+ .build();
+ }
+
+ private ActivityBuilder getActivityBuilderOnSameTask() {
+ return new ActivityBuilder(mAtm)
+ .setTask(mTask)
.setComponent(ComponentName.createRelative(mContext,
SizeCompatTests.class.getName()))
- .setUid(android.os.Process.myUid())
- .build();
+ .setUid(android.os.Process.myUid());
}
static void prepareMinAspectRatio(ActivityRecord activity, float minAspect,
@@ -4931,21 +4811,29 @@
}
}
- /** Asserts that the size of activity is larger than its parent so it is scaling. */
private void assertScaled() {
- assertTrue(mActivity.inSizeCompatMode());
- assertNotEquals(1f, mActivity.getCompatScale(), 0.0001f /* delta */);
+ assertScaled(mActivity);
+ }
+
+ /** Asserts that the size of activity is larger than its parent so it is scaling. */
+ private void assertScaled(ActivityRecord activity) {
+ assertTrue(activity.inSizeCompatMode());
+ assertNotEquals(1f, activity.getCompatScale(), 0.0001f /* delta */);
+ }
+
+ private void assertFitted() {
+ assertFitted(mActivity);
}
/** Asserts that the activity is best fitted in the parent. */
- private void assertFitted() {
- final boolean inSizeCompatMode = mActivity.inSizeCompatMode();
+ private void assertFitted(ActivityRecord activity) {
+ final boolean inSizeCompatMode = activity.inSizeCompatMode();
final String failedConfigInfo = inSizeCompatMode
- ? ("ParentConfig=" + mActivity.getParent().getConfiguration()
- + " ActivityConfig=" + mActivity.getConfiguration())
+ ? ("ParentConfig=" + activity.getParent().getConfiguration()
+ + " ActivityConfig=" + activity.getConfiguration())
: "";
assertFalse(failedConfigInfo, inSizeCompatMode);
- assertFalse(mActivity.hasSizeCompatBounds());
+ assertFalse(activity.hasSizeCompatBounds());
}
/** Asserts the activity max bounds inherit from the TaskDisplayArea. */
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index d57a7e6..f94e5e3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -56,6 +56,7 @@
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
+import android.app.ActivityOptions;
import android.content.pm.SigningDetails;
import android.content.res.Configuration;
import android.graphics.Color;
@@ -291,6 +292,30 @@
}
@Test
+ public void testFindTopNonFinishingActivity_ignoresLaunchedFromBubbleActivities() {
+ final ActivityOptions opts = ActivityOptions.makeBasic();
+ opts.setTaskAlwaysOnTop(true);
+ opts.setLaunchedFromBubble(true);
+ ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setUid(DEFAULT_TASK_FRAGMENT_ORGANIZER_UID).setActivityOptions(opts).build();
+ mTaskFragment.addChild(activity);
+
+ assertNull(mTaskFragment.getTopNonFinishingActivity(true, false));
+ }
+
+ @Test
+ public void testFindTopNonFinishingActivity_includesLaunchedFromBubbleActivities() {
+ final ActivityOptions opts = ActivityOptions.makeBasic();
+ opts.setTaskAlwaysOnTop(true);
+ opts.setLaunchedFromBubble(true);
+ ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setUid(DEFAULT_TASK_FRAGMENT_ORGANIZER_UID).setActivityOptions(opts).build();
+ mTaskFragment.addChild(activity);
+
+ assertEquals(mTaskFragment.getTopNonFinishingActivity(true, true), activity);
+ }
+
+ @Test
public void testMoveTaskToFront_supportsEnterPipOnTaskSwitchForAdjacentTaskFragment() {
final Task bottomTask = createTask(mDisplayContent);
final ActivityRecord bottomActivity = createActivityRecord(bottomTask);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 9f85acb..4ebbf49 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -960,10 +960,20 @@
assertTrue(child.handlesOrientationChangeFromDescendant(orientation));
}
+ private static void addLocalInsets(WindowContainer wc) {
+ final Binder owner = new Binder();
+ Rect genericOverlayInsetsRect1 = new Rect(0, 200, 1080, 700);
+ final InsetsFrameProvider provider1 =
+ new InsetsFrameProvider(owner, 1, WindowInsets.Type.systemOverlays())
+ .setArbitraryRectangle(genericOverlayInsetsRect1);
+ wc.addLocalInsetsFrameProvider(provider1, owner);
+ }
+
@Test
public void testOnDisplayChanged() {
final Task rootTask = createTask(mDisplayContent);
final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
+ addLocalInsets(task);
final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
final DisplayContent newDc = createNewDisplay();
@@ -972,6 +982,7 @@
verify(rootTask).onDisplayChanged(newDc);
verify(task).onDisplayChanged(newDc);
+ assertTrue(task.mLocalInsetsSources.size() == 1);
verify(activity).onDisplayChanged(newDc);
assertEquals(newDc, rootTask.mDisplayContent);
assertEquals(newDc, task.mDisplayContent);
@@ -981,6 +992,7 @@
@Test
public void testOnDisplayChanged_cleanupChanging() {
final Task task = createTask(mDisplayContent);
+ addLocalInsets(task);
spyOn(task.mSurfaceFreezer);
mDisplayContent.mChangingContainers.add(task);
@@ -988,6 +1000,7 @@
// This happens on display info changed.
task.onDisplayChanged(mDisplayContent);
+ assertTrue(task.mLocalInsetsSources.size() == 1);
assertTrue(mDisplayContent.mChangingContainers.contains(task));
verify(task.mSurfaceFreezer, never()).unfreeze(any());
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index e8bac66..175a09d 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -80,6 +80,7 @@
import android.service.usb.UsbHandlerProto;
import android.util.Pair;
import android.util.Slog;
+import android.text.TextUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
@@ -110,6 +111,8 @@
import java.util.Scanner;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* UsbDeviceManager manages USB state in device mode.
@@ -135,6 +138,11 @@
*/
private static final String NORMAL_BOOT = "normal";
+ /**
+ * UDC controller for the ConfigFS USB Gadgets.
+ */
+ private static final String USB_CONTROLLER_NAME_PROPERTY = "sys.usb.controller";
+
private static final String USB_STATE_MATCH =
"DEVPATH=/devices/virtual/android_usb/android0";
private static final String ACCESSORY_START_MATCH =
@@ -932,6 +940,42 @@
sEventLogger.enqueue(new EventLogger.StringEvent("USB intent: " + intent));
}
+ private void getMidiCardDevice() throws FileNotFoundException {
+ String controllerName = getSystemProperty(USB_CONTROLLER_NAME_PROPERTY, "");
+ if (TextUtils.isEmpty(controllerName)) {
+ throw new FileNotFoundException("controller name not found");
+ }
+
+ File soundDir = new File("/sys/class/udc/" + controllerName + "/gadget/sound");
+ if (!soundDir.exists()) {
+ throw new FileNotFoundException("sound device not found");
+ }
+
+ // There should be exactly one sound card
+ File[] cardDirs = FileUtils.listFilesOrEmpty(soundDir,
+ (dir, file) -> file.startsWith("card"));
+ if (cardDirs.length != 1) {
+ throw new FileNotFoundException("sound card not match");
+ }
+
+ // There should be exactly one midi device
+ File[] midis = FileUtils.listFilesOrEmpty(cardDirs[0],
+ (dir, file) -> file.startsWith("midi"));
+ if (midis.length != 1) {
+ throw new FileNotFoundException("MIDI device not match");
+ }
+
+ Pattern pattern = Pattern.compile("midiC(\\d+)D(\\d+)");
+ Matcher matcher = pattern.matcher(midis[0].getName());
+ if (matcher.matches()) {
+ mMidiCard = Integer.parseInt(matcher.group(1));
+ mMidiDevice = Integer.parseInt(matcher.group(2));
+ Slog.i(TAG, "Found MIDI card " + mMidiCard + " device " + mMidiDevice);
+ } else {
+ throw new FileNotFoundException("MIDI name not match");
+ }
+ }
+
private void updateUsbFunctions() {
updateMidiFunction();
updateMtpFunction();
@@ -941,17 +985,26 @@
boolean enabled = (mCurrentFunctions & UsbManager.FUNCTION_MIDI) != 0;
if (enabled != mMidiEnabled) {
if (enabled) {
- Scanner scanner = null;
- try {
- scanner = new Scanner(new File(MIDI_ALSA_PATH));
- mMidiCard = scanner.nextInt();
- mMidiDevice = scanner.nextInt();
- } catch (FileNotFoundException e) {
- Slog.e(TAG, "could not open MIDI file", e);
- enabled = false;
- } finally {
- if (scanner != null) {
- scanner.close();
+ if (android.hardware.usb.flags.Flags.enableUsbSysfsMidiIdentification()) {
+ try {
+ getMidiCardDevice();
+ } catch (FileNotFoundException e) {
+ Slog.e(TAG, "could not identify MIDI device", e);
+ enabled = false;
+ }
+ } else {
+ Scanner scanner = null;
+ try {
+ scanner = new Scanner(new File(MIDI_ALSA_PATH));
+ mMidiCard = scanner.nextInt();
+ mMidiDevice = scanner.nextInt();
+ } catch (FileNotFoundException e) {
+ Slog.e(TAG, "could not open MIDI file", e);
+ enabled = false;
+ } finally {
+ if (scanner != null) {
+ scanner.close();
+ }
}
}
}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
old mode 100755
new mode 100644
diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java
old mode 100755
new mode 100644
diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
old mode 100755
new mode 100644
diff --git a/telephony/common/com/google/android/mms/package.html b/telephony/common/com/google/android/mms/package.html
old mode 100755
new mode 100644
diff --git a/telephony/common/com/google/android/mms/pdu/PduParser.java b/telephony/common/com/google/android/mms/pdu/PduParser.java
old mode 100755
new mode 100644
diff --git a/telephony/common/com/google/android/mms/pdu/PduPersister.java b/telephony/common/com/google/android/mms/pdu/PduPersister.java
old mode 100755
new mode 100644
diff --git a/telephony/common/com/google/android/mms/pdu/package.html b/telephony/common/com/google/android/mms/pdu/package.html
old mode 100755
new mode 100644
diff --git a/telephony/common/com/google/android/mms/util/package.html b/telephony/common/com/google/android/mms/util/package.html
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/SubscriptionInfo.aidl b/telephony/java/android/telephony/SubscriptionInfo.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java
index c35242d..5f9a524 100644
--- a/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java
+++ b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java
@@ -242,6 +242,16 @@
&& Arrays.equals(mPolicyRuleFlags, that.mPolicyRuleFlags);
}
+ @Override
+ public int hashCode() {
+ int result = Arrays.hashCode(mPolicyRules);
+ result = 31 * result + Arrays.hashCode(mPolicyRuleFlags);
+ for (int i = 0; i < mCarrierIds.length; i++) {
+ result = 31 * result + Arrays.hashCode(mCarrierIds[i]);
+ }
+ return result;
+ }
+
private EuiccRulesAuthTable(Parcel source) {
mPolicyRules = source.createIntArray();
int len = mPolicyRules.length;
diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java
index f548dba..c7af661 100644
--- a/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java
+++ b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java
@@ -256,6 +256,26 @@
}
/**
+ * Get the attribute flag ATTR_REGISTRATION_TYPE_EMERGENCY.
+ * @return {@code true} if the ATTR_REGISTRATION_TYPE_EMERGENCY attribute has been set, or
+ * {@code false} if it has not been set.
+ */
+ @FlaggedApi(Flags.FLAG_EMERGENCY_REGISTRATION_STATE)
+ public boolean getFlagRegistrationTypeEmergency() {
+ return (mImsAttributeFlags & ATTR_REGISTRATION_TYPE_EMERGENCY) != 0;
+ }
+
+ /**
+ * Get the attribute flag ATTR_VIRTUAL_FOR_ANONYMOUS_EMERGENCY_CALL.
+ * @return {@code true} if the ATTR_VIRTUAL_FOR_ANONYMOUS_EMERGENCY_CALL attribute has been set,
+ * or {@code false} if it has not been set.
+ */
+ @FlaggedApi(Flags.FLAG_EMERGENCY_REGISTRATION_STATE)
+ public boolean getFlagVirtualRegistrationForEmergencyCall() {
+ return (mImsAttributeFlags & ATTR_VIRTUAL_FOR_ANONYMOUS_EMERGENCY_CALL) != 0;
+ }
+
+ /**
* Gets the Set of feature tags associated with the current IMS registration, if the IMS
* service supports supplying this information.
* <p>
diff --git a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/mbms/DownloadRequest.aidl b/telephony/java/android/telephony/mbms/DownloadRequest.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/mbms/FileInfo.aidl b/telephony/java/android/telephony/mbms/FileInfo.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/mbms/FileServiceInfo.aidl b/telephony/java/android/telephony/mbms/FileServiceInfo.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/mbms/IDownloadProgressListener.aidl b/telephony/java/android/telephony/mbms/IDownloadProgressListener.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/mbms/IDownloadStatusListener.aidl b/telephony/java/android/telephony/mbms/IDownloadStatusListener.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/mbms/IGroupCallCallback.aidl b/telephony/java/android/telephony/mbms/IGroupCallCallback.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl b/telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl b/telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl b/telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl b/telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/mbms/ServiceInfo.aidl b/telephony/java/android/telephony/mbms/ServiceInfo.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/mbms/StreamingServiceInfo.aidl b/telephony/java/android/telephony/mbms/StreamingServiceInfo.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/mbms/UriPathPair.aidl b/telephony/java/android/telephony/mbms/UriPathPair.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/com/android/internal/telephony/IOns.aidl b/telephony/java/com/android/internal/telephony/IOns.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index a9ebd5c..2158f3d 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -252,4 +252,10 @@
/** The key to specify the emergency service category */
public static final String EXTRA_EMERGENCY_SERVICE_CATEGORY = "emergency_service_category";
+
+ /** The key to specify the alternate emergency URNs */
+ public static final String EXTRA_EMERGENCY_URNS = "emergency_urns";
+
+ /** The key to specify whether or not to use emergency routing */
+ public static final String EXTRA_USE_EMERGENCY_ROUTING = "use_emergency_routing";
}
diff --git a/test-mock/api/system-current.txt b/test-mock/api/system-current.txt
index f350957..7d891c8 100644
--- a/test-mock/api/system-current.txt
+++ b/test-mock/api/system-current.txt
@@ -28,7 +28,7 @@
method @Deprecated public void removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
method @Deprecated public void revokeRuntimePermission(String, String, android.os.UserHandle);
method @Deprecated public boolean setDefaultBrowserPackageNameAsUser(String, int);
- method public String[] setPackagesSuspended(String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, String);
+ method @Deprecated public String[] setPackagesSuspended(String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, String);
method @Deprecated public void setUpdateAvailable(String, boolean);
method @Deprecated public boolean updateIntentVerificationStatusAsUser(String, int, int);
method @Deprecated public void updatePermissionFlags(String, String, int, int, android.os.UserHandle);
diff --git a/tests/AccessoryDisplay/sink/res/drawable-hdpi/ic_app.png b/tests/AccessoryDisplay/sink/res/drawable-hdpi/ic_app.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/tests/AccessoryDisplay/source/res/drawable-hdpi/ic_app.png b/tests/AccessoryDisplay/source/res/drawable-hdpi/ic_app.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/tests/AttestationVerificationTest/AndroidManifest.xml b/tests/AttestationVerificationTest/AndroidManifest.xml
old mode 100755
new mode 100644
diff --git a/tests/DozeTest/res/drawable-hdpi/ic_app.png b/tests/DozeTest/res/drawable-hdpi/ic_app.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
index a23f211..379b45c 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
@@ -24,7 +24,6 @@
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
import android.tools.traces.parsers.toFlickerComponent
-import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
@@ -175,12 +174,6 @@
}
}
- @FlakyTest(bugId = 342596801)
- @Test
- override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
- }
-
@Ignore("Not applicable to this CUJ.")
override fun visibleLayersShownMoreThanOneConsecutiveEntry() {}
diff --git a/tests/Input/assets/testPointerScale.png b/tests/Input/assets/testPointerScale.png
new file mode 100644
index 0000000..54d37c2
--- /dev/null
+++ b/tests/Input/assets/testPointerScale.png
Binary files differ
diff --git a/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt b/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt
index dac4253..d196b85 100644
--- a/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt
+++ b/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt
@@ -93,7 +93,29 @@
PointerIcon.getLoadedSystemIcon(
ContextThemeWrapper(context, theme),
PointerIcon.TYPE_ARROW,
- /* useLargeIcons= */ false)
+ /* useLargeIcons= */ false,
+ /* pointerScale= */ 1f)
+
+ pointerIcon.getBitmap().assertAgainstGolden(
+ screenshotRule,
+ testName.methodName,
+ exactScreenshotMatcher
+ )
+ }
+
+ @Test
+ fun testPointerScale() {
+ assumeTrue(enableVectorCursors())
+ assumeTrue(enableVectorCursorA11ySettings())
+
+ val pointerScale = 2f
+
+ val pointerIcon =
+ PointerIcon.getLoadedSystemIcon(
+ context,
+ PointerIcon.TYPE_ARROW,
+ /* useLargeIcons= */ false,
+ pointerScale)
pointerIcon.getBitmap().assertAgainstGolden(
screenshotRule,
diff --git a/tests/Internal/TEST_MAPPING b/tests/Internal/TEST_MAPPING
new file mode 100644
index 0000000..20af028
--- /dev/null
+++ b/tests/Internal/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "postsubmit": [
+ {
+ "name": "InternalTests"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/tests/OdmApps/app/AndroidManifest.xml b/tests/OdmApps/app/AndroidManifest.xml
old mode 100755
new mode 100644
diff --git a/tests/OdmApps/priv-app/AndroidManifest.xml b/tests/OdmApps/priv-app/AndroidManifest.xml
old mode 100755
new mode 100644
diff --git a/tests/RemoteDisplayProvider/res/drawable-hdpi/ic_app.png b/tests/RemoteDisplayProvider/res/drawable-hdpi/ic_app.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/tests/TouchLatency/app/src/main/res/values/styles.xml b/tests/TouchLatency/app/src/main/res/values/styles.xml
index fa352cf..5058331 100644
--- a/tests/TouchLatency/app/src/main/res/values/styles.xml
+++ b/tests/TouchLatency/app/src/main/res/values/styles.xml
@@ -18,7 +18,7 @@
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.MaterialComponents.Light.DarkActionBar">
<!-- Customize your theme here. -->
- <item name="android:windowLayoutInDisplayCutoutMode">default</item>
+ <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
</style>
</resources>
diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java
index 56dbde0..fff1dd1 100644
--- a/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java
+++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java
@@ -39,11 +39,13 @@
import org.junit.After;
import org.junit.Before;
import org.junit.ClassRule;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(BedsteadJUnit4.class)
+@Ignore("b/345557347")
public final class ConcurrentMultiUserTest {
@ClassRule
diff --git a/tools/codegen/src/com/android/codegen/Main.kt b/tools/codegen/src/com/android/codegen/Main.kt
old mode 100755
new mode 100644
diff --git a/tools/localedata/extract_icu_data.py b/tools/localedata/extract_icu_data.py
index 81ac897..8f67fa8 100755
--- a/tools/localedata/extract_icu_data.py
+++ b/tools/localedata/extract_icu_data.py
@@ -22,6 +22,8 @@
import os.path
import sys
+import xml.etree.ElementTree as ElementTree
+
def get_locale_parts(locale):
"""Split a locale into three parts, for langauge, script, and region."""
@@ -40,42 +42,43 @@
def read_likely_subtags(input_file_name):
"""Read and parse ICU's likelySubtags.txt."""
- with open(input_file_name) as input_file:
- likely_script_dict = {
- # Android's additions for pseudo-locales. These internal codes make
- # sure that the pseudo-locales would not match other English or
- # Arabic locales. (We can't use private-use ISO 15924 codes, since
- # they may be used by apps for other purposes.)
- "en_XA": "~~~A",
- "ar_XB": "~~~B",
- # Removed data from later versions of ICU
- "ji": "Hebr", # Old code for Yiddish, still used in Java and Android
- }
- representative_locales = {
- # Android's additions
- "en_Latn_GB", # representative for en_Latn_001
- "es_Latn_MX", # representative for es_Latn_419
- "es_Latn_US", # representative for es_Latn_419 (not the best idea,
- # but Android has been shipping with it for quite a
- # while. Fortunately, MX < US, so if both exist, MX
- # would be chosen.)
- }
- for line in input_file:
- line = line.strip(u' \n\uFEFF')
- if line.startswith('//'):
- continue
- if '{' in line and '}' in line:
- from_locale = line[:line.index('{')]
- to_locale = line[line.index('"')+1:line.rindex('"')]
- from_lang, from_scr, from_region = get_locale_parts(from_locale)
- _, to_scr, to_region = get_locale_parts(to_locale)
- if from_lang == 'und':
- continue # not very useful for our purposes
- if from_region is None and to_region not in ['001', 'ZZ']:
- representative_locales.add(to_locale)
- if from_scr is None:
- likely_script_dict[from_locale] = to_scr
- return likely_script_dict, frozenset(representative_locales)
+ likely_script_dict = {
+ # Android's additions for pseudo-locales. These internal codes make
+ # sure that the pseudo-locales would not match other English or
+ # Arabic locales. (We can't use private-use ISO 15924 codes, since
+ # they may be used by apps for other purposes.)
+ "en_XA": "~~~A",
+ "ar_XB": "~~~B",
+ # Removed data from later versions of ICU
+ "ji": "Hebr", # Old code for Yiddish, still used in Java and Android
+ }
+ representative_locales = {
+ # Android's additions
+ "en_Latn_GB", # representative for en_Latn_001
+ "es_Latn_MX", # representative for es_Latn_419
+ "es_Latn_US", # representative for es_Latn_419 (not the best idea,
+ # but Android has been shipping with it for quite a
+ # while. Fortunately, MX < US, so if both exist, MX
+ # would be chosen.)
+ }
+ xml_tree = ElementTree.parse(input_file_name)
+ likely_subtags = xml_tree.find('likelySubtags')
+ for child in likely_subtags:
+ from_locale = child.get('from')
+ to_locale = child.get('to')
+ # print(f'from: {from_locale} to: {to_locale}')
+ from_lang, from_scr, from_region = get_locale_parts(from_locale)
+ _, to_scr, to_region = get_locale_parts(to_locale)
+ if to_locale == "FAIL":
+ continue # "FAIL" cases are not useful here.
+ if from_lang == 'und':
+ continue # not very useful for our purposes
+ if from_region is None and to_region not in ['001', 'ZZ']:
+ representative_locales.add(to_locale)
+ if from_scr is None:
+ likely_script_dict[from_locale] = to_scr
+
+ return likely_script_dict, frozenset(representative_locales)
# From packLanguageOrRegion() in ResourceTypes.cpp
@@ -86,7 +89,7 @@
elif len(inp) == 2:
return ord(inp[0]), ord(inp[1])
else:
- assert len(inp) == 3
+ assert len(inp) == 3, f'Expects a 3-character string, but "{inp}" '
base = ord(base)
first = ord(inp[0]) - base
second = ord(inp[1]) - base
@@ -161,9 +164,10 @@
print('});')
-def read_and_dump_likely_data(icu_data_dir):
+def read_and_dump_likely_data(cldr_source_dir):
"""Read and dump the likely-script data."""
- likely_subtags_txt = os.path.join(icu_data_dir, 'misc', 'likelySubtags.txt')
+ likely_subtags_txt = os.path.join(cldr_source_dir,
+ 'common', 'supplemental', 'likelySubtags.xml')
likely_script_dict, representative_locales = read_likely_subtags(
likely_subtags_txt)
@@ -280,10 +284,11 @@
icu_data_dir = os.path.join(
source_root,
'external', 'icu', 'icu4c', 'source', 'data')
+ cldr_source_dir = os.path.join(source_root, 'external', 'cldr')
print('// Auto-generated by %s' % sys.argv[0])
print()
- likely_script_dict = read_and_dump_likely_data(icu_data_dir)
+ likely_script_dict = read_and_dump_likely_data(cldr_source_dir)
read_and_dump_parent_data(icu_data_dir, likely_script_dict)
diff --git a/wifi/java/src/android/net/wifi/SoftApConfToXmlMigrationUtil.java b/wifi/java/src/android/net/wifi/SoftApConfToXmlMigrationUtil.java
old mode 100755
new mode 100644
diff --git a/wifi/java/src/android/net/wifi/WifiMigration.java b/wifi/java/src/android/net/wifi/WifiMigration.java
old mode 100755
new mode 100644
diff --git a/wifi/java/src/android/net/wifi/WifiNetworkScoreCache.java b/wifi/java/src/android/net/wifi/WifiNetworkScoreCache.java
old mode 100755
new mode 100644