Merge "[flexiglass] Add a DeviceEntry module." into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 08a09e1..b1f587e 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -334,7 +334,7 @@
aconfig_declarations {
name: "android.app.flags-aconfig",
package: "android.app",
- srcs: ["core/java/android/app/activity_manager.aconfig"],
+ srcs: ["core/java/android/app/*.aconfig"],
}
java_aconfig_library {
diff --git a/Android.bp b/Android.bp
index f1a3af2..4c44974 100644
--- a/Android.bp
+++ b/Android.bp
@@ -525,6 +525,7 @@
required: [
"framework-minus-apex",
"framework-platform-compat-config",
+ "framework-location-compat-config",
"services-platform-compat-config",
"icu4j-platform-compat-config",
"TeleService-platform-compat-config",
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 122e627..b215ee4 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -68,7 +68,6 @@
"name": "FrameworksInputMethodSystemServerTests",
"options": [
{"include-filter": "com.android.server.inputmethod"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "org.junit.Ignore"}
]
diff --git a/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING
index b8fef63..6924cb2 100644
--- a/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING
@@ -7,7 +7,6 @@
],
"options": [
{"include-filter": "com.android.server.DeviceIdleControllerTest"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
},
@@ -29,4 +28,4 @@
]
}
]
-}
\ No newline at end of file
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING
index b76c582..c0686116 100644
--- a/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING
@@ -4,7 +4,6 @@
"name": "FrameworksMockingServicesTests",
"options": [
{"include-filter": "com.android.server.DeviceIdleControllerTest"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
}
@@ -17,4 +16,4 @@
]
}
]
-}
\ No newline at end of file
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
index 8504b1f..e649485 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
@@ -3,8 +3,6 @@
{
"name": "CtsJobSchedulerTestCases",
"options": [
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
- {"exclude-annotation": "androidx.test.filters.LargeTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.LargeTest"}
]
@@ -13,18 +11,16 @@
"name": "FrameworksMockingServicesTests",
"options": [
{"include-filter": "com.android.server.job"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
- {"exclude-annotation": "androidx.test.filters.LargeTest"},
- {"exclude-annotation": "androidx.test.filters.FlakyTest"}
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.LargeTest"}
]
},
{
"name": "FrameworksServicesTests",
"options": [
{"include-filter": "com.android.server.job"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
- {"exclude-annotation": "androidx.test.filters.LargeTest"},
- {"exclude-annotation": "androidx.test.filters.FlakyTest"}
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.LargeTest"}
]
}
],
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/tare/TEST_MAPPING
index 73b00b6..e194b8d 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/tare/TEST_MAPPING
@@ -4,7 +4,6 @@
"name": "FrameworksMockingServicesTests",
"options": [
{"include-filter": "com.android.server.tare"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
},
@@ -12,7 +11,6 @@
"name": "FrameworksServicesTests",
"options": [
{"include-filter": "com.android.server.tare"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
}
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
index 9ec799f..a75415e 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
@@ -4,7 +4,6 @@
"name": "CtsUsageStatsTestCases",
"options": [
{"include-filter": "android.app.usage.cts.UsageStatsTest"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.MediumTest"},
{"exclude-annotation": "androidx.test.filters.LargeTest"}
@@ -21,7 +20,6 @@
"name": "FrameworksServicesTests",
"options": [
{"include-filter": "com.android.server.usage"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
}
@@ -37,4 +35,4 @@
]
}
]
-}
\ No newline at end of file
+}
diff --git a/boot/Android.bp b/boot/Android.bp
index 93d425e..b33fab6 100644
--- a/boot/Android.bp
+++ b/boot/Android.bp
@@ -208,6 +208,13 @@
],
}
+genrule { // This module exists to make the srcjar output available to Make.
+ name: "platform-bootclasspath.srcjar",
+ srcs: [":platform-bootclasspath{.srcjar}"],
+ out: ["platform-bootclasspath.srcjar"],
+ cmd: "cp $(in) $(out)",
+}
+
platform_systemserverclasspath {
name: "platform-systemserverclasspath",
}
diff --git a/cmds/uinput/README.md b/cmds/uinput/README.md
index 82df555..e7361fe 100644
--- a/cmds/uinput/README.md
+++ b/cmds/uinput/README.md
@@ -48,6 +48,7 @@
| `vid` | 16-bit integer | Vendor ID |
| `pid` | 16-bit integer | Product ID |
| `bus` | string | Bus that device should use |
+| `port` | string | `phys` value to report |
| `configuration` | object array | uinput device configuration|
| `ff_effects_max` | integer | `ff_effects_max` value |
| `abs_info` | array | Absolute axes information |
diff --git a/core/api/current.txt b/core/api/current.txt
index c9f0639..f340e16 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -5952,6 +5952,7 @@
public class GrammaticalInflectionManager {
method public int getApplicationGrammaticalGender();
+ method @FlaggedApi("android.app.system_terms_of_address_enabled") public int getSystemGrammaticalGender();
method public void setRequestedApplicationGrammaticalGender(int);
}
@@ -42903,7 +42904,7 @@
field public static final String KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY = "carrier_service_number_array";
field public static final String KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING = "carrier_settings_activity_component_name_string";
field public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool";
- field public static final String KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE = "carrier_supported_satellite_services_per_provider_bundle";
+ field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE = "carrier_supported_satellite_services_per_provider_bundle";
field public static final String KEY_CARRIER_SUPPORTS_OPP_DATA_AUTO_PROVISIONING_BOOL = "carrier_supports_opp_data_auto_provisioning_bool";
field public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL = "carrier_supports_ss_over_ut_bool";
field public static final String KEY_CARRIER_SUPPORTS_TETHERING_BOOL = "carrier_supports_tethering_bool";
@@ -43071,6 +43072,7 @@
field public static final String KEY_RTT_SUPPORTED_WHILE_ROAMING_BOOL = "rtt_supported_while_roaming_bool";
field public static final String KEY_RTT_UPGRADE_SUPPORTED_BOOL = "rtt_upgrade_supported_bool";
field public static final String KEY_RTT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_VT_CALL_BOOL = "rtt_upgrade_supported_for_downgraded_vt_call";
+ field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ATTACH_SUPPORTED_BOOL = "satellite_attach_supported_bool";
field public static final String KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL = "show_4g_for_3g_data_icon_bool";
field public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL = "show_4g_for_lte_data_icon_bool";
field public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
@@ -54454,8 +54456,8 @@
public class AnimationUtils {
ctor public AnimationUtils();
method public static long currentAnimationTimeMillis();
- method public static long getExpectedPresentationTimeMillis();
- method public static long getExpectedPresentationTimeNanos();
+ method @FlaggedApi("android.view.flags.expected_presentation_time_api") public static long getExpectedPresentationTimeMillis();
+ method @FlaggedApi("android.view.flags.expected_presentation_time_api") public static long getExpectedPresentationTimeNanos();
method public static android.view.animation.Animation loadAnimation(android.content.Context, @AnimRes int) throws android.content.res.Resources.NotFoundException;
method public static android.view.animation.Interpolator loadInterpolator(android.content.Context, @AnimRes @InterpolatorRes int) throws android.content.res.Resources.NotFoundException;
method public static android.view.animation.LayoutAnimationController loadLayoutAnimation(android.content.Context, @AnimRes int) throws android.content.res.Resources.NotFoundException;
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 1d88e00..8748e69 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3223,14 +3223,14 @@
public final class VirtualDeviceParams implements android.os.Parcelable {
method public int describeContents();
- method @NonNull public java.util.Set<android.content.ComponentName> getAllowedActivities();
- method @NonNull public java.util.Set<android.content.ComponentName> getAllowedCrossTaskNavigations();
+ method @Deprecated @NonNull public java.util.Set<android.content.ComponentName> getAllowedActivities();
+ method @Deprecated @NonNull public java.util.Set<android.content.ComponentName> getAllowedCrossTaskNavigations();
method public int getAudioPlaybackSessionId();
method public int getAudioRecordingSessionId();
- method @NonNull public java.util.Set<android.content.ComponentName> getBlockedActivities();
- method @NonNull public java.util.Set<android.content.ComponentName> getBlockedCrossTaskNavigations();
- method public int getDefaultActivityPolicy();
- method public int getDefaultNavigationPolicy();
+ method @Deprecated @NonNull public java.util.Set<android.content.ComponentName> getBlockedActivities();
+ method @Deprecated @NonNull public java.util.Set<android.content.ComponentName> getBlockedCrossTaskNavigations();
+ method @Deprecated public int getDefaultActivityPolicy();
+ method @Deprecated public int getDefaultNavigationPolicy();
method public int getDevicePolicy(int);
method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @Nullable public android.content.ComponentName getHomeComponent();
method public int getLockState();
@@ -3238,15 +3238,15 @@
method @NonNull public java.util.Set<android.os.UserHandle> getUsersWithMatchingAccounts();
method @NonNull public java.util.List<android.companion.virtual.sensor.VirtualSensorConfig> getVirtualSensorConfigs();
method public void writeToParcel(@NonNull android.os.Parcel, int);
- field public static final int ACTIVITY_POLICY_DEFAULT_ALLOWED = 0; // 0x0
- field public static final int ACTIVITY_POLICY_DEFAULT_BLOCKED = 1; // 0x1
+ field @Deprecated public static final int ACTIVITY_POLICY_DEFAULT_ALLOWED = 0; // 0x0
+ field @Deprecated public static final int ACTIVITY_POLICY_DEFAULT_BLOCKED = 1; // 0x1
field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.VirtualDeviceParams> CREATOR;
field public static final int DEVICE_POLICY_CUSTOM = 1; // 0x1
field public static final int DEVICE_POLICY_DEFAULT = 0; // 0x0
field public static final int LOCK_STATE_ALWAYS_UNLOCKED = 1; // 0x1
field public static final int LOCK_STATE_DEFAULT = 0; // 0x0
- field public static final int NAVIGATION_POLICY_DEFAULT_ALLOWED = 0; // 0x0
- field public static final int NAVIGATION_POLICY_DEFAULT_BLOCKED = 1; // 0x1
+ field @Deprecated public static final int NAVIGATION_POLICY_DEFAULT_ALLOWED = 0; // 0x0
+ field @Deprecated public static final int NAVIGATION_POLICY_DEFAULT_BLOCKED = 1; // 0x1
field @FlaggedApi("android.companion.virtual.flags.dynamic_policy") public static final int POLICY_TYPE_ACTIVITY = 3; // 0x3
field public static final int POLICY_TYPE_AUDIO = 1; // 0x1
field public static final int POLICY_TYPE_RECENTS = 2; // 0x2
@@ -3257,12 +3257,12 @@
ctor public VirtualDeviceParams.Builder();
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder addVirtualSensorConfig(@NonNull android.companion.virtual.sensor.VirtualSensorConfig);
method @NonNull public android.companion.virtual.VirtualDeviceParams build();
- method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAllowedActivities(@NonNull java.util.Set<android.content.ComponentName>);
- method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAllowedCrossTaskNavigations(@NonNull java.util.Set<android.content.ComponentName>);
+ method @Deprecated @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAllowedActivities(@NonNull java.util.Set<android.content.ComponentName>);
+ method @Deprecated @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAllowedCrossTaskNavigations(@NonNull java.util.Set<android.content.ComponentName>);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAudioPlaybackSessionId(int);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAudioRecordingSessionId(int);
- method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedActivities(@NonNull java.util.Set<android.content.ComponentName>);
- method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedCrossTaskNavigations(@NonNull java.util.Set<android.content.ComponentName>);
+ method @Deprecated @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedActivities(@NonNull java.util.Set<android.content.ComponentName>);
+ method @Deprecated @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedCrossTaskNavigations(@NonNull java.util.Set<android.content.ComponentName>);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setDevicePolicy(int, int);
method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setHomeComponent(@Nullable android.content.ComponentName);
method @NonNull @RequiresPermission(value=android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY, conditional=true) public android.companion.virtual.VirtualDeviceParams.Builder setLockState(int);
@@ -16731,17 +16731,22 @@
}
public final class SatelliteManager {
+ method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void addSatelliteAttachRestrictionForCarrier(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void deprovisionSatelliteService(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public java.util.Set<java.lang.Integer> getSatelliteAttachRestrictionReasonsForCarrier(int);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void pollPendingSatelliteDatagrams(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void provisionSatelliteService(@NonNull String, @NonNull byte[], @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteDatagram(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteDatagramCallback);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteModemStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteStateCallback);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteProvisionStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteProvisionStateCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void removeSatelliteAttachRestrictionForCarrier(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsDemoModeEnabled(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
+ method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteAttachEnabledForCarrier(int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteCommunicationAllowedForCurrentLocation(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteEnabled(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteProvisioned(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method public void requestIsSatelliteSupported(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
+ method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteAttachEnabledForCarrier(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteCapabilities(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.satellite.SatelliteCapabilities,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteEnabled(boolean, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestTimeForNextSatelliteVisibility(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.time.Duration,android.telephony.satellite.SatelliteManager.SatelliteException>);
@@ -16768,6 +16773,7 @@
field public static final int NT_RADIO_TECHNOLOGY_NR_NTN = 2; // 0x2
field public static final int NT_RADIO_TECHNOLOGY_PROPRIETARY = 4; // 0x4
field public static final int NT_RADIO_TECHNOLOGY_UNKNOWN = 0; // 0x0
+ field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final int SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION = 1; // 0x1
field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE = 0; // 0x0
field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED = 7; // 0x7
field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_NONE = 6; // 0x6
diff --git a/core/java/android/app/GrammaticalInflectionManager.java b/core/java/android/app/GrammaticalInflectionManager.java
index 1905b6a..bc6fe61 100644
--- a/core/java/android/app/GrammaticalInflectionManager.java
+++ b/core/java/android/app/GrammaticalInflectionManager.java
@@ -16,12 +16,15 @@
package android.app;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
import android.annotation.SystemService;
import android.content.Context;
import android.content.res.Configuration;
import android.os.RemoteException;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@@ -31,11 +34,15 @@
*/
@SystemService(Context.GRAMMATICAL_INFLECTION_SERVICE)
public class GrammaticalInflectionManager {
- private static final Set<Integer> VALID_GENDER_VALUES = new HashSet<>(Arrays.asList(
+
+ /** @hide */
+ @NonNull
+ public static final Set<Integer> VALID_GRAMMATICAL_GENDER_VALUES =
+ Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED,
Configuration.GRAMMATICAL_GENDER_NEUTRAL,
Configuration.GRAMMATICAL_GENDER_FEMININE,
- Configuration.GRAMMATICAL_GENDER_MASCULINE));
+ Configuration.GRAMMATICAL_GENDER_MASCULINE)));
private final Context mContext;
private final IGrammaticalInflectionManager mService;
@@ -79,7 +86,7 @@
*/
public void setRequestedApplicationGrammaticalGender(
@Configuration.GrammaticalGender int grammaticalGender) {
- if (!VALID_GENDER_VALUES.contains(grammaticalGender)) {
+ if (!VALID_GRAMMATICAL_GENDER_VALUES.contains(grammaticalGender)) {
throw new IllegalArgumentException("Unknown grammatical gender");
}
@@ -90,4 +97,44 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Sets the current grammatical gender for all privileged applications. The value will be
+ * stored in an encrypted file at {@link android.os.Environment#getDataSystemCeDirectory(int)
+ *
+ * @param grammaticalGender the terms of address the user preferred in system.
+ *
+ * @see Configuration#getGrammaticalGender
+ * @hide
+ */
+ public void setSystemWideGrammaticalGender(
+ @Configuration.GrammaticalGender int grammaticalGender) {
+ if (!VALID_GRAMMATICAL_GENDER_VALUES.contains(grammaticalGender)) {
+ throw new IllegalArgumentException("Unknown grammatical gender");
+ }
+
+ try {
+ mService.setSystemWideGrammaticalGender(mContext.getUserId(), grammaticalGender);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the current grammatical gender of privileged application from the encrypted file,
+ * which is stored under {@link android.os.Environment#getDataSystemCeDirectory(int)}.
+ *
+ * @return the value of grammatical gender
+ *
+ * @see Configuration#getGrammaticalGender
+ */
+ @FlaggedApi(Flags.FLAG_SYSTEM_TERMS_OF_ADDRESS_ENABLED)
+ @Configuration.GrammaticalGender
+ public int getSystemGrammaticalGender() {
+ try {
+ return mService.getSystemGrammaticalGender(mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/IGrammaticalInflectionManager.aidl b/core/java/android/app/IGrammaticalInflectionManager.aidl
index 9366a45..48a4841 100644
--- a/core/java/android/app/IGrammaticalInflectionManager.aidl
+++ b/core/java/android/app/IGrammaticalInflectionManager.aidl
@@ -16,4 +16,14 @@
* Sets a specified app’s app-specific grammatical gender.
*/
void setRequestedApplicationGrammaticalGender(String appPackageName, int userId, int gender);
- }
\ No newline at end of file
+
+ /**
+ * Sets the grammatical gender to system.
+ */
+ void setSystemWideGrammaticalGender(int userId, int gender);
+
+ /**
+ * Gets the grammatical gender from system.
+ */
+ int getSystemGrammaticalGender(int userId);
+ }
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index e1c45d9..9cf54e3 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -54,6 +54,9 @@
per-file Broadcast* = file:/BROADCASTS_OWNERS
per-file ReceiverInfo* = file:/BROADCASTS_OWNERS
+# GrammaticalInflectionManager
+per-file *GrammaticalInflection* = file:/services/core/java/com/android/server/grammaticalinflection/OWNERS
+
# KeyguardManager
per-file KeyguardManager.java = file:/services/core/java/com/android/server/locksettings/OWNERS
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index 315a055..a29c196 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -19,7 +19,7 @@
"name": "CtsAppOpsTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
},
diff --git a/core/java/android/app/grammatical_inflection_manager.aconfig b/core/java/android/app/grammatical_inflection_manager.aconfig
new file mode 100644
index 0000000..989ce61
--- /dev/null
+++ b/core/java/android/app/grammatical_inflection_manager.aconfig
@@ -0,0 +1,8 @@
+package: "android.app"
+
+flag {
+ name: "system_terms_of_address_enabled"
+ namespace: "grammatical_gender"
+ description: "Feature flag for System Terms of Address"
+ bug: "297798866"
+}
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index 0fa78c8..0975cbb 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -94,13 +94,19 @@
/**
* Indicates that activities are allowed by default on this virtual device, unless they are
* explicitly blocked by {@link Builder#setBlockedActivities}.
+ *
+ * @deprecated Use {@link #POLICY_TYPE_ACTIVITY} and {@link #DEVICE_POLICY_DEFAULT}
*/
+ @Deprecated
public static final int ACTIVITY_POLICY_DEFAULT_ALLOWED = 0;
/**
* Indicates that activities are blocked by default on this virtual device, unless they are
* allowed by {@link Builder#setAllowedActivities}.
+ *
+ * @deprecated Use {@link #POLICY_TYPE_ACTIVITY} and {@link #DEVICE_POLICY_CUSTOM}
*/
+ @Deprecated
public static final int ACTIVITY_POLICY_DEFAULT_BLOCKED = 1;
/** @hide */
@@ -113,13 +119,19 @@
/**
* Indicates that tasks are allowed to navigate to other tasks on this virtual device,
* unless they are explicitly blocked by {@link Builder#setBlockedCrossTaskNavigations}.
+ *
+ * @deprecated Use {@link #POLICY_TYPE_ACTIVITY} and {@link #DEVICE_POLICY_DEFAULT}
*/
+ @Deprecated
public static final int NAVIGATION_POLICY_DEFAULT_ALLOWED = 0;
/**
* Indicates that tasks are blocked from navigating to other tasks by default on this virtual
* device, unless allowed by {@link Builder#setAllowedCrossTaskNavigations}.
+ *
+ * @deprecated Use {@link #POLICY_TYPE_ACTIVITY} and {@link #DEVICE_POLICY_CUSTOM}
*/
+ @Deprecated
public static final int NAVIGATION_POLICY_DEFAULT_BLOCKED = 1;
/** @hide */
@@ -325,7 +337,10 @@
* be be allowed by default.
*
* @see Builder#setAllowedCrossTaskNavigations(Set)
+ *
+ * @deprecated See {@link VirtualDeviceManager.VirtualDevice#addActivityPolicyExemption}
*/
+ @Deprecated
@NonNull
public Set<ComponentName> getAllowedCrossTaskNavigations() {
return mDefaultNavigationPolicy == NAVIGATION_POLICY_DEFAULT_ALLOWED
@@ -340,7 +355,10 @@
* will be be allowed by default.
*
* @see Builder#setBlockedCrossTaskNavigations(Set)
+ *
+ * @deprecated See {@link VirtualDeviceManager.VirtualDevice#addActivityPolicyExemption}
*/
+ @Deprecated
@NonNull
public Set<ComponentName> getBlockedCrossTaskNavigations() {
return mDefaultNavigationPolicy == NAVIGATION_POLICY_DEFAULT_BLOCKED
@@ -355,7 +373,10 @@
*
* @see Builder#setAllowedCrossTaskNavigations
* @see Builder#setBlockedCrossTaskNavigations
+ *
+ * @deprecated Use {@link #getDevicePolicy} with {@link #POLICY_TYPE_ACTIVITY}
*/
+ @Deprecated
@NavigationPolicy
public int getDefaultNavigationPolicy() {
return mDefaultNavigationPolicy;
@@ -366,7 +387,10 @@
* allowed, except the ones explicitly blocked.
*
* @see Builder#setAllowedActivities(Set)
+ *
+ * @deprecated See {@link VirtualDeviceManager.VirtualDevice#addActivityPolicyExemption}
*/
+ @Deprecated
@NonNull
public Set<ComponentName> getAllowedActivities() {
return mDefaultActivityPolicy == ACTIVITY_POLICY_DEFAULT_ALLOWED
@@ -379,7 +403,10 @@
* that all activities in {@link #getAllowedActivities} are allowed.
*
* @see Builder#setBlockedActivities(Set)
+ *
+ * @deprecated See {@link VirtualDeviceManager.VirtualDevice#addActivityPolicyExemption}
*/
+ @Deprecated
@NonNull
public Set<ComponentName> getBlockedActivities() {
return mDefaultActivityPolicy == ACTIVITY_POLICY_DEFAULT_BLOCKED
@@ -394,7 +421,10 @@
*
* @see Builder#setBlockedActivities
* @see Builder#setAllowedActivities
+ *
+ * @deprecated Use {@link #getDevicePolicy} with {@link #POLICY_TYPE_ACTIVITY}
*/
+ @Deprecated
@ActivityPolicy
public int getDefaultActivityPolicy() {
return mDefaultActivityPolicy;
@@ -743,7 +773,11 @@
*
* @param allowedCrossTaskNavigations A set of tasks {@link ComponentName} allowed to
* navigate to new tasks in the virtual device.
+ *
+ * @deprecated Use {@link #POLICY_TYPE_ACTIVITY} and
+ * {@link VirtualDeviceManager.VirtualDevice#addActivityPolicyExemption}
*/
+ @Deprecated
@NonNull
public Builder setAllowedCrossTaskNavigations(
@NonNull Set<ComponentName> allowedCrossTaskNavigations) {
@@ -774,7 +808,11 @@
*
* @param blockedCrossTaskNavigations A set of tasks {@link ComponentName} to be
* blocked from navigating to new tasks in the virtual device.
+ *
+ * @deprecated Use {@link #POLICY_TYPE_ACTIVITY} and
+ * {@link VirtualDeviceManager.VirtualDevice#addActivityPolicyExemption}
*/
+ @Deprecated
@NonNull
public Builder setBlockedCrossTaskNavigations(
@NonNull Set<ComponentName> blockedCrossTaskNavigations) {
@@ -802,7 +840,11 @@
*
* @param allowedActivities A set of activity {@link ComponentName} allowed to be launched
* in the virtual device.
+ *
+ * @deprecated Use {@link #POLICY_TYPE_ACTIVITY} and
+ * {@link VirtualDeviceManager.VirtualDevice#addActivityPolicyExemption}
*/
+ @Deprecated
@NonNull
public Builder setAllowedActivities(@NonNull Set<ComponentName> allowedActivities) {
if (mDefaultActivityPolicyConfigured
@@ -828,7 +870,11 @@
*
* @param blockedActivities A set of {@link ComponentName} to be blocked launching from
* virtual device.
+ *
+ * @deprecated Use {@link #POLICY_TYPE_ACTIVITY} and
+ * {@link VirtualDeviceManager.VirtualDevice#addActivityPolicyExemption}
*/
+ @Deprecated
@NonNull
public Builder setBlockedActivities(@NonNull Set<ComponentName> blockedActivities) {
if (mDefaultActivityPolicyConfigured
diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index ab669cc..b0ab11f 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -45,7 +45,8 @@
]
},
{
- "name":"CarrierAppIntegrationTestCases"
+ "name":"CarrierAppIntegrationTestCases",
+ "keywords": ["internal"]
},
{
"name":"CtsSilentUpdateHostTestCases"
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 0c8eb02..08d32c3 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -13,3 +13,10 @@
description: "Feature flag to enable the archiving feature."
bug: "278553670"
}
+
+flag {
+ name: "stay_stopped"
+ namespace: "backstage_power"
+ description: "Feature flag to improve stopped state enforcement"
+ bug: "296644915"
+}
diff --git a/core/java/android/os/TEST_MAPPING b/core/java/android/os/TEST_MAPPING
index ad3abd9..2d6e09a 100644
--- a/core/java/android/os/TEST_MAPPING
+++ b/core/java/android/os/TEST_MAPPING
@@ -8,7 +8,6 @@
"name": "FrameworksVibratorCoreTests",
"options": [
{"exclude-annotation": "androidx.test.filters.LargeTest"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "org.junit.Ignore"}
]
@@ -21,7 +20,6 @@
"name": "FrameworksVibratorServicesTests",
"options": [
{"exclude-annotation": "androidx.test.filters.LargeTest"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "org.junit.Ignore"}
]
@@ -34,7 +32,6 @@
"name": "CtsVibratorTestCases",
"options": [
{"exclude-annotation": "androidx.test.filters.LargeTest"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "org.junit.Ignore"}
]
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 80b7d40..4c8ef97 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2940,6 +2940,12 @@
* Used to check if the context user is a restricted profile. Restricted profiles
* may have a reduced number of available apps, app restrictions, and account restrictions.
*
+ * <p>The caller must be in the same profile group as the context user or else hold
+ * <li>{@link android.Manifest.permission#MANAGE_USERS},
+ * <li>or {@link android.Manifest.permission#CREATE_USERS},
+ * <li>or, for devices after {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
+ * {@link android.Manifest.permission#QUERY_USERS}.
+ *
* @return whether the context user is a restricted profile.
* @hide
*/
@@ -2963,6 +2969,12 @@
* Check if a user is a restricted profile. Restricted profiles may have a reduced number of
* available apps, app restrictions, and account restrictions.
*
+ * <p>Requires
+ * <li>{@link android.Manifest.permission#MANAGE_USERS},
+ * <li>or {@link android.Manifest.permission#CREATE_USERS},
+ * <li>or, for devices after {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
+ * {@link android.Manifest.permission#QUERY_USERS}.
+ *
* @param user the user to check
* @return whether the user is a restricted profile.
* @hide
diff --git a/core/java/android/service/notification/TEST_MAPPING b/core/java/android/service/notification/TEST_MAPPING
index 7b8d52f..468c451 100644
--- a/core/java/android/service/notification/TEST_MAPPING
+++ b/core/java/android/service/notification/TEST_MAPPING
@@ -4,9 +4,6 @@
"name": "CtsNotificationTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
- },
- {
"exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
@@ -21,9 +18,6 @@
"name": "FrameworksUiServicesTests",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
- },
- {
"exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
diff --git a/core/java/android/text/TEST_MAPPING b/core/java/android/text/TEST_MAPPING
index 0fe974a..c9bd2ca 100644
--- a/core/java/android/text/TEST_MAPPING
+++ b/core/java/android/text/TEST_MAPPING
@@ -4,7 +4,7 @@
"name": "CtsTextTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
"exclude-annotation": "androidx.test.filters.LargeTest"
diff --git a/core/java/android/text/flags/fix_double_underline.aconfig b/core/java/android/text/flags/fix_double_underline.aconfig
new file mode 100644
index 0000000..b0aa72a
--- /dev/null
+++ b/core/java/android/text/flags/fix_double_underline.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.text.flags"
+
+flag {
+ name: "fix_double_underline"
+ namespace: "text"
+ description: "Feature flag for fixing double underline because of the multiple font used in the single line."
+ bug: "297336724"
+}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 5eaded2..552263a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1328,6 +1328,14 @@
public static final String AUTOFILL_HINT_PASSWORD_AUTO = "passwordAuto";
/**
+ * Hint indicating that the developer intends to fill this view with output from
+ * CredentialManager.
+ *
+ * @hide
+ */
+ public static final String AUTOFILL_HINT_CREDENTIAL_MANAGER = "credential";
+
+ /**
* Hints for the autofill services that describes the content of the view.
*/
private @Nullable String[] mAutofillHints;
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index 8ba8b8c..a07b62f 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -16,7 +16,11 @@
package android.view.animation;
+import static android.view.flags.Flags.FLAG_EXPECTED_PRESENTATION_TIME_API;
+import static android.view.flags.Flags.expectedPresentationTimeApi;
+
import android.annotation.AnimRes;
+import android.annotation.FlaggedApi;
import android.annotation.InterpolatorRes;
import android.annotation.TestApi;
import android.compat.annotation.ChangeId;
@@ -151,7 +155,12 @@
* @return the expected presentation time of a frame in the
* {@link System#nanoTime()} time base.
*/
+ @FlaggedApi(FLAG_EXPECTED_PRESENTATION_TIME_API)
public static long getExpectedPresentationTimeNanos() {
+ if (!expectedPresentationTimeApi()) {
+ return SystemClock.uptimeMillis();
+ }
+
AnimationState state = sAnimationState.get();
return state.mExpectedPresentationTimeNanos;
}
@@ -164,6 +173,7 @@
* @return the expected presentation time of a frame in the
* {@link SystemClock#uptimeMillis()} time base.
*/
+ @FlaggedApi(FLAG_EXPECTED_PRESENTATION_TIME_API)
public static long getExpectedPresentationTimeMillis() {
return getExpectedPresentationTimeNanos() / TimeUtils.NANOS_PER_MS;
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 4cb8788..6cf185a 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1464,7 +1464,7 @@
if (infos.size() == 0) {
throw new IllegalArgumentException("No VirtualViewInfo found");
}
- if (view.isCredential() && mIsFillAndSaveDialogDisabledForCredentialManager) {
+ if (isCredmanRequested(view) && mIsFillAndSaveDialogDisabledForCredentialManager) {
if (sDebug) {
Log.d(TAG, "Ignoring Fill Dialog request since important for credMan:"
+ view.getAutofillId().toString());
@@ -1488,7 +1488,7 @@
* @hide
*/
public void notifyViewEnteredForFillDialog(View v) {
- if (v.isCredential()
+ if (isCredmanRequested(v)
&& mIsFillAndSaveDialogDisabledForCredentialManager) {
if (sDebug) {
Log.d(TAG, "Ignoring Fill Dialog request since important for credMan:"
@@ -3434,6 +3434,22 @@
}
}
+ private boolean isCredmanRequested(View view) {
+ if (view.isCredential()) {
+ return true;
+ }
+ String[] hints = view.getAutofillHints();
+ if (hints == null) {
+ return false;
+ }
+ for (String hint : hints) {
+ if (hint.equals(View.AUTOFILL_HINT_CREDENTIAL_MANAGER)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Find a single view by its id.
*
diff --git a/core/java/android/view/flags/variable_refresh_rate_flags.aconfig b/core/java/android/view/flags/variable_refresh_rate_flags.aconfig
index e3972ad..13a6f8d 100644
--- a/core/java/android/view/flags/variable_refresh_rate_flags.aconfig
+++ b/core/java/android/view/flags/variable_refresh_rate_flags.aconfig
@@ -12,4 +12,11 @@
namespace: "toolkit"
description: "Feature flag for toolkit to set frame rate"
bug: "293512962"
+}
+
+flag {
+ name: "expected_presentation_time_api"
+ namespace: "toolkit"
+ description: "Feature flag for using expected presentation time of the Choreographer"
+ bug: "278730197"
}
\ No newline at end of file
diff --git a/core/java/android/window/ITaskFragmentOrganizerController.aidl b/core/java/android/window/ITaskFragmentOrganizerController.aidl
index d25c8a8..7b7e341 100644
--- a/core/java/android/window/ITaskFragmentOrganizerController.aidl
+++ b/core/java/android/window/ITaskFragmentOrganizerController.aidl
@@ -25,9 +25,12 @@
interface ITaskFragmentOrganizerController {
/**
- * Registers a TaskFragmentOrganizer to manage TaskFragments.
+ * Registers a TaskFragmentOrganizer to manage TaskFragments. Registering a system
+ * organizer requires MANAGE_ACTIVITY_TASKS permission, and the organizer will have additional
+ * system capabilities.
*/
- void registerOrganizer(in ITaskFragmentOrganizer organizer);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value=android.Manifest.permission.MANAGE_ACTIVITY_TASKS, conditional=true)")
+ void registerOrganizer(in ITaskFragmentOrganizer organizer, in boolean isSystemOrganizer);
/**
* Unregisters a previously registered TaskFragmentOrganizer.
diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java
index f785a3d..a6c9cec 100644
--- a/core/java/android/window/TaskFragmentOrganizer.java
+++ b/core/java/android/window/TaskFragmentOrganizer.java
@@ -22,9 +22,11 @@
import static android.view.WindowManager.TRANSIT_OPEN;
import android.annotation.CallSuper;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.TestApi;
import android.os.Bundle;
import android.os.IBinder;
@@ -32,6 +34,8 @@
import android.view.RemoteAnimationDefinition;
import android.view.WindowManager;
+import com.android.window.flags.Flags;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.Executor;
@@ -140,12 +144,34 @@
}
/**
- * Registers a TaskFragmentOrganizer to manage TaskFragments.
+ * Registers a {@link TaskFragmentOrganizer} to manage TaskFragments.
*/
@CallSuper
public void registerOrganizer() {
+ // TODO(b/302420256) point to registerOrganizer(boolean) when flag is removed.
try {
- getController().registerOrganizer(mInterface);
+ getController().registerOrganizer(mInterface, false /* isSystemOrganizer */);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Registers a {@link TaskFragmentOrganizer} to manage TaskFragments.
+ *
+ * Registering a system organizer requires MANAGE_ACTIVITY_TASKS permission, and the organizer
+ * will have additional system capabilities, including: (1) it will receive SurfaceControl for
+ * the organized TaskFragment, and (2) it needs to update the
+ * {@link android.view.SurfaceControl} following the window change accordingly.
+ *
+ * @hide
+ */
+ @CallSuper
+ @RequiresPermission(value = "android.permission.MANAGE_ACTIVITY_TASKS", conditional = true)
+ @FlaggedApi(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG)
+ public void registerOrganizer(boolean isSystemOrganizer) {
+ try {
+ getController().registerOrganizer(mInterface, isSystemOrganizer);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/window/TaskFragmentTransaction.java b/core/java/android/window/TaskFragmentTransaction.java
index 3c5d60d..4dada10 100644
--- a/core/java/android/window/TaskFragmentTransaction.java
+++ b/core/java/android/window/TaskFragmentTransaction.java
@@ -30,6 +30,7 @@
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
+import android.view.SurfaceControl;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -192,6 +193,9 @@
@Nullable
private TaskFragmentParentInfo mTaskFragmentParentInfo;
+ @Nullable
+ private SurfaceControl mSurfaceControl;
+
public Change(@ChangeType int type) {
mType = type;
}
@@ -206,6 +210,7 @@
mActivityIntent = in.readTypedObject(Intent.CREATOR);
mActivityToken = in.readStrongBinder();
mTaskFragmentParentInfo = in.readTypedObject(TaskFragmentParentInfo.CREATOR);
+ mSurfaceControl = in.readTypedObject(SurfaceControl.CREATOR);
}
@Override
@@ -219,6 +224,7 @@
dest.writeTypedObject(mActivityIntent, flags);
dest.writeStrongBinder(mActivityToken);
dest.writeTypedObject(mTaskFragmentParentInfo, flags);
+ dest.writeTypedObject(mSurfaceControl, flags);
}
/** The change is related to the TaskFragment created with this unique token. */
@@ -306,6 +312,13 @@
return this;
}
+ /** @hide */
+ @NonNull
+ public Change setTaskFragmentSurfaceControl(@Nullable SurfaceControl sc) {
+ mSurfaceControl = sc;
+ return this;
+ }
+
@ChangeType
public int getType() {
return mType;
@@ -359,6 +372,21 @@
return mTaskFragmentParentInfo;
}
+ /**
+ * Gets the {@link SurfaceControl} of the TaskFragment. This field is {@code null} for
+ * a regular {@link TaskFragmentOrganizer} and is only available for a system
+ * {@link TaskFragmentOrganizer} in the
+ * {@link TaskFragmentTransaction#TYPE_TASK_FRAGMENT_APPEARED} event. See
+ * {@link ITaskFragmentOrganizerController#registerOrganizer(ITaskFragmentOrganizer,
+ * boolean)}
+ *
+ * @hide
+ */
+ @Nullable
+ public SurfaceControl getTaskFragmentSurfaceControl() {
+ return mSurfaceControl;
+ }
+
@Override
public String toString() {
return "Change{ type=" + mType + " }";
diff --git a/core/java/com/android/internal/infra/TEST_MAPPING b/core/java/com/android/internal/infra/TEST_MAPPING
index ddfd0ed..c09181f 100644
--- a/core/java/com/android/internal/infra/TEST_MAPPING
+++ b/core/java/com/android/internal/infra/TEST_MAPPING
@@ -4,7 +4,7 @@
"name": "CtsRoleTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
},
@@ -15,7 +15,7 @@
"include-filter": "android.permission.cts.PermissionControllerTest"
},
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
},
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index 878e6b3..71d696e 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -172,28 +172,17 @@
<integer name="config_satellite_nb_iot_inactivity_timeout_millis">180000</integer>
<java-symbol type="integer" name="config_satellite_nb_iot_inactivity_timeout_millis" />
- <!-- Telephony config for services supported by satellite providers. The format of each config
- string in the array is as follows: "PLMN_1:service_1,service_2,..."
- where PLMN is the satellite PLMN of a provider and service is an integer with the
- following value:
- 1 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_VOICE}
- 2 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_DATA}
- 3 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_SMS}
- 4 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_VIDEO}
- 5 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_EMERGENCY}
- 6 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_MMS}
- Example of a config string: "10011:2,3"
+ <!-- Telephony config for the PLMNs of all satellite providers. This is used by satellite modem
+ to identify providers that should be ignored if the carrier config
+ carrier_supported_satellite_services_per_provider_bundle does not support them.
+ -->
+ <string-array name="config_satellite_providers" translatable="false"></string-array>
+ <java-symbol type="array" name="config_satellite_providers" />
- The PLMNs not configured in this array will be ignored and will not be used for satellite
- scanning. -->
- <string-array name="config_satellite_services_supported_by_providers" translatable="false">
- </string-array>
- <java-symbol type="array" name="config_satellite_services_supported_by_providers" />
-
- <!-- The identifier of the satellite's eSIM profile preloaded on the device. The identifier is
- composed of MCC and MNC of the satellite PLMN with the format "mccmnc". -->
- <string name="config_satellite_esim_identifier" translatable="false"></string>
- <java-symbol type="string" name="config_satellite_esim_identifier" />
+ <!-- The identifier of the satellite's SIM profile. The identifier is composed of MCC and MNC
+ of the satellite PLMN with the format "mccmnc". -->
+ <string name="config_satellite_sim_identifier" translatable="false"></string>
+ <java-symbol type="string" name="config_satellite_sim_identifier" />
<!-- Whether enhanced IWLAN handover check is enabled. If enabled, telephony frameworks
will not perform handover if the target transport is out of service, or VoPS not
diff --git a/core/tests/coretests/src/android/window/flags/WindowFlagsTest.java b/core/tests/coretests/src/android/window/flags/WindowFlagsTest.java
index a8b4032..a5bbeb5 100644
--- a/core/tests/coretests/src/android/window/flags/WindowFlagsTest.java
+++ b/core/tests/coretests/src/android/window/flags/WindowFlagsTest.java
@@ -17,6 +17,7 @@
package android.window.flags;
import static com.android.window.flags.Flags.syncWindowConfigUpdateFlag;
+import static com.android.window.flags.Flags.taskFragmentSystemOrganizerFlag;
import android.platform.test.annotations.Presubmit;
@@ -42,4 +43,10 @@
// No crash when accessing the flag.
syncWindowConfigUpdateFlag();
}
+
+ @Test
+ public void testTaskFragmentSystemOrganizerFlag() {
+ // No crash when accessing the flag.
+ taskFragmentSystemOrganizerFlag();
+ }
}
diff --git a/core/tests/vibrator/TEST_MAPPING b/core/tests/vibrator/TEST_MAPPING
index 2f3afa6..54a5ff1 100644
--- a/core/tests/vibrator/TEST_MAPPING
+++ b/core/tests/vibrator/TEST_MAPPING
@@ -4,7 +4,6 @@
"name": "FrameworksVibratorCoreTests",
"options": [
{"exclude-annotation": "androidx.test.filters.LargeTest"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "org.junit.Ignore"}
]
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 7faf380..705e387 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -435,10 +435,10 @@
<dimen name="desktop_mode_handle_menu_windowing_pill_height">52dp</dimen>
<!-- The height of the handle menu's "More Actions" pill in desktop mode, but not freeform. -->
- <dimen name="desktop_mode_handle_menu_more_actions_pill_height">156dp</dimen>
+ <dimen name="desktop_mode_handle_menu_more_actions_pill_height">104dp</dimen>
<!-- The height of the handle menu's "More Actions" pill in freeform desktop windowing mode. -->
- <dimen name="desktop_mode_handle_menu_more_actions_pill_freeform_height">104dp</dimen>
+ <dimen name="desktop_mode_handle_menu_more_actions_pill_freeform_height">52dp</dimen>
<!-- The top margin of the handle menu in desktop mode. -->
<dimen name="desktop_mode_handle_menu_margin_top">4dp</dimen>
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 3d825f0..4ea14f4 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
@@ -720,6 +720,8 @@
mSplitLayout.setDivideRatio(snapPosition);
updateWindowBounds(mSplitLayout, wct);
wct.reorder(mRootTaskInfo.token, true);
+ wct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token,
+ false /* reparentLeafTaskIfRelaunch */);
setRootForceTranslucent(false, wct);
// Make sure the launch options will put tasks in the corresponding split roots
@@ -767,6 +769,8 @@
mSplitLayout.setDivideRatio(snapPosition);
updateWindowBounds(mSplitLayout, wct);
wct.reorder(mRootTaskInfo.token, true);
+ wct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token,
+ false /* reparentLeafTaskIfRelaunch */);
setRootForceTranslucent(false, wct);
options1 = options1 != null ? options1 : new Bundle();
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 f82b212..a7a11de 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
@@ -163,6 +163,9 @@
final ImageButton splitscreenBtn = windowingPillView.findViewById(
R.id.split_screen_button);
final ImageButton floatingBtn = windowingPillView.findViewById(R.id.floating_button);
+ // TODO: Remove once implemented.
+ floatingBtn.setVisibility(View.GONE);
+
final ImageButton desktopBtn = windowingPillView.findViewById(R.id.desktop_button);
fullscreenBtn.setOnClickListener(mOnClickListener);
splitscreenBtn.setOnClickListener(mOnClickListener);
@@ -196,6 +199,9 @@
}
final Button selectBtn = moreActionsPillView.findViewById(R.id.select_button);
selectBtn.setOnClickListener(mOnClickListener);
+ final Button screenshotBtn = moreActionsPillView.findViewById(R.id.screenshot_button);
+ // TODO: Remove once implemented.
+ screenshotBtn.setVisibility(View.GONE);
}
/**
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
index b0e847f..e37d806 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
@@ -19,15 +19,10 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit
-import org.junit.Rule
import org.junit.Test
open class CopyContentInSplitGesturalNavLandscape : CopyContentInSplit(Rotation.ROTATION_90) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
index f847e40..2a50912 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
@@ -19,15 +19,10 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit
-import org.junit.Rule
import org.junit.Test
open class CopyContentInSplitGesturalNavPortrait : CopyContentInSplit(Rotation.ROTATION_0) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
index 31e52e2..d5da1a8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByDivider
-import org.junit.Rule
import org.junit.Test
open class DismissSplitScreenByDividerGesturalNavLandscape :
DismissSplitScreenByDivider(Rotation.ROTATION_90) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
index 0870073..7fdcb9b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByDivider
-import org.junit.Rule
import org.junit.Test
open class DismissSplitScreenByDividerGesturalNavPortrait :
DismissSplitScreenByDivider(Rotation.ROTATION_0) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
index 1bb6c45..308e954 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByGoHome
-import org.junit.Rule
import org.junit.Test
open class DismissSplitScreenByGoHomeGesturalNavLandscape :
DismissSplitScreenByGoHome(Rotation.ROTATION_90) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
index bb084d6..39e75bd 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByGoHome
-import org.junit.Rule
import org.junit.Test
open class DismissSplitScreenByGoHomeGesturalNavPortrait :
DismissSplitScreenByGoHome(Rotation.ROTATION_0) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
index bc5857f..e18da17 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
@@ -19,15 +19,10 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize
-import org.junit.Rule
import org.junit.Test
open class DragDividerToResizeGesturalNavLandscape : DragDividerToResize(Rotation.ROTATION_90) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
index e4faa4a..00d60e7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
@@ -19,15 +19,10 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize
-import org.junit.Rule
import org.junit.Test
open class DragDividerToResizeGesturalNavPortrait : DragDividerToResize(Rotation.ROTATION_0) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
index 7431974..d7efbc8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromAllApps
-import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenByDragFromAllAppsGesturalNavLandscape :
EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_90) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
index c2b0609..4eece3f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromAllApps
-import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenByDragFromAllAppsGesturalNavPortrait :
EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_0) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
index 7d1072d..d96b056 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromNotification
-import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenByDragFromNotificationGesturalNavLandscape :
EnterSplitScreenByDragFromNotification(Rotation.ROTATION_90) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
index 79760d1..809b690 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromNotification
-import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenByDragFromNotificationGesturalNavPortrait :
EnterSplitScreenByDragFromNotification(Rotation.ROTATION_0) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
index ff3729a..bbdf2d7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromShortcut
-import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenByDragFromShortcutGesturalNavLandscape :
EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_90) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
index ffbcfe4..5c29fd8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromShortcut
-import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenByDragFromShortcutGesturalNavPortrait :
EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_0) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
index 5e937c0..a7398eb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromTaskbar
-import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenByDragFromTaskbarGesturalNavLandscape :
EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_90) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
index 89db8a0..eae88ad 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromTaskbar
-import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenByDragFromTaskbarGesturalNavPortrait :
EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_0) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
index bf4ee7a..7e8ee04 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenFromOverview
-import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenFromOverviewGesturalNavLandscape :
EnterSplitScreenFromOverview(Rotation.ROTATION_90) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
index 7e96f06..9295c33 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenFromOverview
-import org.junit.Rule
import org.junit.Test
open class EnterSplitScreenFromOverviewGesturalNavPortrait :
EnterSplitScreenFromOverview(Rotation.ROTATION_0) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
index a84deac..4b59e9f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchAppByDoubleTapDivider
-import org.junit.Rule
import org.junit.Test
open class SwitchAppByDoubleTapDividerGesturalNavLandscape :
SwitchAppByDoubleTapDivider(Rotation.ROTATION_90) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
index afcc743..5ff36d4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchAppByDoubleTapDivider
-import org.junit.Rule
import org.junit.Test
open class SwitchAppByDoubleTapDividerGesturalNavPortrait :
SwitchAppByDoubleTapDivider(Rotation.ROTATION_0) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
index 2f96f5f..c0cb721 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromAnotherApp
-import org.junit.Rule
import org.junit.Test
open class SwitchBackToSplitFromAnotherAppGesturalNavLandscape :
SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_90) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
index efbeb76..8c14088 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromAnotherApp
-import org.junit.Rule
import org.junit.Test
open class SwitchBackToSplitFromAnotherAppGesturalNavPortrait :
SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_0) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
index 232fccc..7b6614b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromHome
-import org.junit.Rule
import org.junit.Test
open class SwitchBackToSplitFromHomeGesturalNavLandscape :
SwitchBackToSplitFromHome(Rotation.ROTATION_90) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
index d7f2845..5df5be9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromHome
-import org.junit.Rule
import org.junit.Test
open class SwitchBackToSplitFromHomeGesturalNavPortrait :
SwitchBackToSplitFromHome(Rotation.ROTATION_0) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
index 019c946..9d63003 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromRecent
-import org.junit.Rule
import org.junit.Test
open class SwitchBackToSplitFromRecentGesturalNavLandscape :
SwitchBackToSplitFromRecent(Rotation.ROTATION_90) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
index 5678861..9fa04b2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromRecent
-import org.junit.Rule
import org.junit.Test
open class SwitchBackToSplitFromRecentGesturalNavPortrait :
SwitchBackToSplitFromRecent(Rotation.ROTATION_0) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
index b3c4ea2..9386aa2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs
-import org.junit.Rule
import org.junit.Test
open class SwitchBetweenSplitPairsGesturalNavLandscape :
SwitchBetweenSplitPairs(Rotation.ROTATION_90) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
index f88180f..5ef2167 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
@@ -19,16 +19,11 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs
-import org.junit.Rule
import org.junit.Test
open class SwitchBetweenSplitPairsGesturalNavPortrait :
SwitchBetweenSplitPairs(Rotation.ROTATION_0) {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
index 391cb9b7..9caab9b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
@@ -18,18 +18,13 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.BlockJUnit4ClassRunner
@RunWith(BlockJUnit4ClassRunner::class)
open class UnlockKeyguardToSplitScreenGesturalNavLandscape : UnlockKeyguardToSplitScreen() {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
index 4af133d..bf484e5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
@@ -18,18 +18,13 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
-import android.tools.device.flicker.rules.FlickerServiceRule
import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.BlockJUnit4ClassRunner
@RunWith(BlockJUnit4ClassRunner::class)
open class UnlockKeyguardToSplitScreenGesturalNavPortrait : UnlockKeyguardToSplitScreen() {
- @get:Rule
- val flickerServiceRule = FlickerServiceRule(enabled = true, failTestOnFlicker = false)
-
@PlatinumTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index e1dd145..ff1eedb 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -143,6 +143,7 @@
"libcrypto",
"libsync",
"libui",
+ "aconfig_text_flags_c_lib",
],
static_libs: [
"libEGL_blobCache",
@@ -712,11 +713,13 @@
],
static_libs: [
+ "libflagtest",
"libgmock",
"libhwui_static",
],
shared_libs: [
"libmemunreachable",
+ "server_configurable_flags",
],
srcs: [
"tests/unit/main.cpp",
@@ -756,6 +759,7 @@
"tests/unit/TestUtilsTests.cpp",
"tests/unit/ThreadBaseTests.cpp",
"tests/unit/TypefaceTests.cpp",
+ "tests/unit/UnderlineTest.cpp",
"tests/unit/VectorDrawableTests.cpp",
"tests/unit/WebViewFunctorManagerTests.cpp",
],
diff --git a/libs/hwui/FeatureFlags.h b/libs/hwui/FeatureFlags.h
new file mode 100644
index 0000000..ffb329d
--- /dev/null
+++ b/libs/hwui/FeatureFlags.h
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HWUI_FEATURE_FLAGS_H
+#define ANDROID_HWUI_FEATURE_FLAGS_H
+
+#ifdef __ANDROID__
+#include <com_android_text_flags.h>
+#endif // __ANDROID__
+
+namespace android {
+
+namespace text_feature {
+
+inline bool fix_double_underline() {
+#ifdef __ANDROID__
+ return com_android_text_flags_fix_double_underline();
+#else
+ return true;
+#endif // __ANDROID__
+}
+
+} // namespace text_feature
+
+} // namespace android
+
+#endif // ANDROID_HWUI_FEATURE_FLAGS_H
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 8394c3c..31fc929 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -47,6 +47,7 @@
#include <utility>
#include "CanvasProperty.h"
+#include "FeatureFlags.h"
#include "Mesh.h"
#include "NinePatchUtils.h"
#include "VectorDrawable.h"
@@ -795,7 +796,9 @@
sk_sp<SkTextBlob> textBlob(builder.make());
applyLooper(&paintCopy, [&](const SkPaint& p) { mCanvas->drawTextBlob(textBlob, 0, 0, p); });
- drawTextDecorations(x, y, totalAdvance, paintCopy);
+ if (!text_feature::fix_double_underline()) {
+ drawTextDecorations(x, y, totalAdvance, paintCopy);
+ }
}
void SkiaCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index 2351797..80b6c03 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -16,17 +16,18 @@
#include "Canvas.h"
+#include <SkFontMetrics.h>
+#include <SkRRect.h>
+
+#include "FeatureFlags.h"
#include "MinikinUtils.h"
#include "Paint.h"
#include "Properties.h"
#include "RenderNode.h"
#include "Typeface.h"
-#include "pipeline/skia/SkiaRecordingCanvas.h"
-
+#include "hwui/DrawTextFunctor.h"
#include "hwui/PaintFilter.h"
-
-#include <SkFontMetrics.h>
-#include <SkRRect.h>
+#include "pipeline/skia/SkiaRecordingCanvas.h"
namespace android {
@@ -34,13 +35,6 @@
return new uirenderer::skiapipeline::SkiaRecordingCanvas(renderNode, width, height);
}
-static inline void drawStroke(SkScalar left, SkScalar right, SkScalar top, SkScalar thickness,
- const Paint& paint, Canvas* canvas) {
- const SkScalar strokeWidth = fmax(thickness, 1.0f);
- const SkScalar bottom = top + strokeWidth;
- canvas->drawRect(left, top, right, bottom, paint);
-}
-
void Canvas::drawTextDecorations(float x, float y, float length, const Paint& paint) {
// paint has already been filtered by our caller, so we can ignore any filter
const bool strikeThru = paint.isStrikeThru();
@@ -72,73 +66,6 @@
}
}
-static void simplifyPaint(int color, Paint* paint) {
- paint->setColor(color);
- paint->setShader(nullptr);
- paint->setColorFilter(nullptr);
- paint->setLooper(nullptr);
- paint->setStrokeWidth(4 + 0.04 * paint->getSkFont().getSize());
- paint->setStrokeJoin(SkPaint::kRound_Join);
- paint->setLooper(nullptr);
-}
-
-class DrawTextFunctor {
-public:
- DrawTextFunctor(const minikin::Layout& layout, Canvas* canvas, const Paint& paint, float x,
- float y, float totalAdvance)
- : layout(layout)
- , canvas(canvas)
- , paint(paint)
- , x(x)
- , y(y)
- , totalAdvance(totalAdvance) {}
-
- void operator()(size_t start, size_t end) {
- auto glyphFunc = [&](uint16_t* text, float* positions) {
- for (size_t i = start, textIndex = 0, posIndex = 0; i < end; i++) {
- text[textIndex++] = layout.getGlyphId(i);
- positions[posIndex++] = x + layout.getX(i);
- positions[posIndex++] = y + layout.getY(i);
- }
- };
-
- size_t glyphCount = end - start;
-
- if (CC_UNLIKELY(canvas->isHighContrastText() && paint.getAlpha() != 0)) {
- // high contrast draw path
- int color = paint.getColor();
- int channelSum = SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color);
- bool darken = channelSum < (128 * 3);
-
- // outline
- gDrawTextBlobMode = DrawTextBlobMode::HctOutline;
- Paint outlinePaint(paint);
- simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint);
- outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style);
- canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, totalAdvance);
-
- // inner
- gDrawTextBlobMode = DrawTextBlobMode::HctInner;
- Paint innerPaint(paint);
- simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, &innerPaint);
- innerPaint.setStyle(SkPaint::kFill_Style);
- canvas->drawGlyphs(glyphFunc, glyphCount, innerPaint, x, y, totalAdvance);
- gDrawTextBlobMode = DrawTextBlobMode::Normal;
- } else {
- // standard draw path
- canvas->drawGlyphs(glyphFunc, glyphCount, paint, x, y, totalAdvance);
- }
- }
-
-private:
- const minikin::Layout& layout;
- Canvas* canvas;
- const Paint& paint;
- float x;
- float y;
- float totalAdvance;
-};
-
void Canvas::drawGlyphs(const minikin::Font& font, const int* glyphIds, const float* positions,
int glyphCount, const Paint& paint) {
// Minikin modify skFont for auto-fakebold/auto-fakeitalic.
@@ -182,6 +109,31 @@
DrawTextFunctor f(layout, this, paint, x, y, layout.getAdvance());
MinikinUtils::forFontRun(layout, &paint, f);
+
+ if (text_feature::fix_double_underline()) {
+ Paint copied(paint);
+ PaintFilter* filter = getPaintFilter();
+ if (filter != nullptr) {
+ filter->filterFullPaint(&copied);
+ }
+ const bool isUnderline = copied.isUnderline();
+ const bool isStrikeThru = copied.isStrikeThru();
+ if (isUnderline || isStrikeThru) {
+ const SkScalar left = x;
+ const SkScalar right = x + layout.getAdvance();
+ if (isUnderline) {
+ const SkScalar top = y + f.getUnderlinePosition();
+ drawStroke(left, right, top, f.getUnderlineThickness(), copied, this);
+ }
+ if (isStrikeThru) {
+ float textSize = paint.getSkFont().getSize();
+ const float position = textSize * Paint::kStdStrikeThru_Top;
+ const SkScalar thickness = textSize * Paint::kStdStrikeThru_Thickness;
+ const SkScalar top = y + position;
+ drawStroke(left, right, top, thickness, copied, this);
+ }
+ }
+ }
}
void Canvas::drawDoubleRoundRectXY(float outerLeft, float outerTop, float outerRight,
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 44ee31d..9ec023b 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -285,7 +285,7 @@
* totalAdvance: used to define width of text decorations (underlines, strikethroughs).
*/
virtual void drawGlyphs(ReadGlyphFunc glyphFunc, int count, const Paint& paint, float x,
- float y,float totalAdvance) = 0;
+ float y, float totalAdvance) = 0;
virtual void drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
const Paint& paint, const SkPath& path, size_t start,
size_t end) = 0;
diff --git a/libs/hwui/hwui/DrawTextFunctor.h b/libs/hwui/hwui/DrawTextFunctor.h
new file mode 100644
index 0000000..2e6e976
--- /dev/null
+++ b/libs/hwui/hwui/DrawTextFunctor.h
@@ -0,0 +1,141 @@
+/*
+ * 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.
+ */
+
+#include <SkFontMetrics.h>
+#include <SkRRect.h>
+
+#include "Canvas.h"
+#include "FeatureFlags.h"
+#include "MinikinUtils.h"
+#include "Paint.h"
+#include "Properties.h"
+#include "RenderNode.h"
+#include "Typeface.h"
+#include "hwui/PaintFilter.h"
+#include "pipeline/skia/SkiaRecordingCanvas.h"
+
+namespace android {
+
+static inline void drawStroke(SkScalar left, SkScalar right, SkScalar top, SkScalar thickness,
+ const Paint& paint, Canvas* canvas) {
+ const SkScalar strokeWidth = fmax(thickness, 1.0f);
+ const SkScalar bottom = top + strokeWidth;
+ canvas->drawRect(left, top, right, bottom, paint);
+}
+
+static void simplifyPaint(int color, Paint* paint) {
+ paint->setColor(color);
+ paint->setShader(nullptr);
+ paint->setColorFilter(nullptr);
+ paint->setLooper(nullptr);
+ paint->setStrokeWidth(4 + 0.04 * paint->getSkFont().getSize());
+ paint->setStrokeJoin(SkPaint::kRound_Join);
+ paint->setLooper(nullptr);
+}
+
+class DrawTextFunctor {
+public:
+ DrawTextFunctor(const minikin::Layout& layout, Canvas* canvas, const Paint& paint, float x,
+ float y, float totalAdvance)
+ : layout(layout)
+ , canvas(canvas)
+ , paint(paint)
+ , x(x)
+ , y(y)
+ , totalAdvance(totalAdvance)
+ , underlinePosition(0)
+ , underlineThickness(0) {}
+
+ void operator()(size_t start, size_t end) {
+ auto glyphFunc = [&](uint16_t* text, float* positions) {
+ for (size_t i = start, textIndex = 0, posIndex = 0; i < end; i++) {
+ text[textIndex++] = layout.getGlyphId(i);
+ positions[posIndex++] = x + layout.getX(i);
+ positions[posIndex++] = y + layout.getY(i);
+ }
+ };
+
+ size_t glyphCount = end - start;
+
+ if (CC_UNLIKELY(canvas->isHighContrastText() && paint.getAlpha() != 0)) {
+ // high contrast draw path
+ int color = paint.getColor();
+ int channelSum = SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color);
+ bool darken = channelSum < (128 * 3);
+
+ // outline
+ gDrawTextBlobMode = DrawTextBlobMode::HctOutline;
+ Paint outlinePaint(paint);
+ simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint);
+ outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style);
+ canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, totalAdvance);
+
+ // inner
+ gDrawTextBlobMode = DrawTextBlobMode::HctInner;
+ Paint innerPaint(paint);
+ simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, &innerPaint);
+ innerPaint.setStyle(SkPaint::kFill_Style);
+ canvas->drawGlyphs(glyphFunc, glyphCount, innerPaint, x, y, totalAdvance);
+ gDrawTextBlobMode = DrawTextBlobMode::Normal;
+ } else {
+ // standard draw path
+ canvas->drawGlyphs(glyphFunc, glyphCount, paint, x, y, totalAdvance);
+ }
+
+ if (text_feature::fix_double_underline()) {
+ // Extract underline position and thickness.
+ if (paint.isUnderline()) {
+ SkFontMetrics metrics;
+ paint.getSkFont().getMetrics(&metrics);
+ const float textSize = paint.getSkFont().getSize();
+ SkScalar position;
+ if (!metrics.hasUnderlinePosition(&position)) {
+ position = textSize * Paint::kStdUnderline_Top;
+ }
+ SkScalar thickness;
+ if (!metrics.hasUnderlineThickness(&thickness)) {
+ thickness = textSize * Paint::kStdUnderline_Thickness;
+ }
+
+ // If multiple fonts are used, use the most bottom position and most thick stroke
+ // width as the underline position. This follows the CSS standard:
+ // https://www.w3.org/TR/css-text-decor-3/#text-underline-position-property
+ // <quote>
+ // The exact position and thickness of line decorations is UA-defined in this level.
+ // However, for underlines and overlines the UA must use a single thickness and
+ // position on each line for the decorations deriving from a single decorating box.
+ // </quote>
+ underlinePosition = std::max(underlinePosition, position);
+ underlineThickness = std::max(underlineThickness, thickness);
+ }
+ }
+ }
+
+ float getUnderlinePosition() const { return underlinePosition; }
+ float getUnderlineThickness() const { return underlineThickness; }
+
+private:
+ const minikin::Layout& layout;
+ Canvas* canvas;
+ const Paint& paint;
+ float x;
+ float y;
+ float totalAdvance;
+ float underlinePosition;
+ float underlineThickness;
+};
+
+} // namespace android
diff --git a/libs/hwui/jni/Gainmap.cpp b/libs/hwui/jni/Gainmap.cpp
index cec0ee7..0fffee7 100644
--- a/libs/hwui/jni/Gainmap.cpp
+++ b/libs/hwui/jni/Gainmap.cpp
@@ -208,8 +208,6 @@
p.writeFloat(info.fDisplayRatioHdr);
// base image type
p.writeInt32(static_cast<int32_t>(info.fBaseImageType));
- // type
- p.writeInt32(static_cast<int32_t>(info.fType));
#else
doThrowRE(env, "Cannot use parcels outside of Android!");
#endif
@@ -232,7 +230,6 @@
info.fDisplayRatioSdr = p.readFloat();
info.fDisplayRatioHdr = p.readFloat();
info.fBaseImageType = static_cast<SkGainmapInfo::BaseImageType>(p.readInt32());
- info.fType = static_cast<SkGainmapInfo::Type>(p.readInt32());
fromJava(nativeObject)->info = info;
#else
diff --git a/libs/hwui/tests/unit/UnderlineTest.cpp b/libs/hwui/tests/unit/UnderlineTest.cpp
new file mode 100644
index 0000000..db2be20
--- /dev/null
+++ b/libs/hwui/tests/unit/UnderlineTest.cpp
@@ -0,0 +1,153 @@
+/*
+ * 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.
+ */
+
+#include <fcntl.h>
+#include <flag_macros.h>
+#include <gtest/gtest.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <utils/Log.h>
+
+#include "SkAlphaType.h"
+#include "SkBitmap.h"
+#include "SkData.h"
+#include "SkFontMgr.h"
+#include "SkImageInfo.h"
+#include "SkRefCnt.h"
+#include "SkStream.h"
+#include "SkTypeface.h"
+#include "SkiaCanvas.h"
+#include "hwui/Bitmap.h"
+#include "hwui/DrawTextFunctor.h"
+#include "hwui/MinikinSkia.h"
+#include "hwui/MinikinUtils.h"
+#include "hwui/Paint.h"
+#include "hwui/Typeface.h"
+
+using namespace android;
+
+namespace {
+
+constexpr char kRobotoVariable[] = "/system/fonts/Roboto-Regular.ttf";
+constexpr char kJPFont[] = "/system/fonts/NotoSansCJK-Regular.ttc";
+
+// The underline position and thickness are cames from post table.
+constexpr float ROBOTO_POSITION_EM = 150.0 / 2048.0;
+constexpr float ROBOTO_THICKNESS_EM = 100.0 / 2048.0;
+constexpr float NOTO_CJK_POSITION_EM = 125.0 / 1000.0;
+constexpr float NOTO_CJK_THICKNESS_EM = 50.0 / 1000.0;
+
+void unmap(const void* ptr, void* context) {
+ void* p = const_cast<void*>(ptr);
+ size_t len = reinterpret_cast<size_t>(context);
+ munmap(p, len);
+}
+
+// Create a font family from a single font file.
+std::shared_ptr<minikin::FontFamily> buildFamily(const char* fileName) {
+ int fd = open(fileName, O_RDONLY);
+ LOG_ALWAYS_FATAL_IF(fd == -1, "Failed to open file %s", fileName);
+ struct stat st = {};
+ LOG_ALWAYS_FATAL_IF(fstat(fd, &st) == -1, "Failed to stat file %s", fileName);
+ void* data = mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ sk_sp<SkData> skData =
+ SkData::MakeWithProc(data, st.st_size, unmap, reinterpret_cast<void*>(st.st_size));
+ std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(skData));
+ sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
+ sk_sp<SkTypeface> typeface(fm->makeFromStream(std::move(fontData)));
+ LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", fileName);
+ std::shared_ptr<minikin::MinikinFont> font =
+ std::make_shared<MinikinFontSkia>(std::move(typeface), 0, data, st.st_size, fileName, 0,
+ std::vector<minikin::FontVariation>());
+ std::vector<std::shared_ptr<minikin::Font>> fonts;
+ fonts.push_back(minikin::Font::Builder(font).build());
+ return minikin::FontFamily::create(std::move(fonts));
+}
+
+// Create a typeface from roboto and NotoCJK.
+Typeface* makeTypeface() {
+ return Typeface::createFromFamilies(
+ std::vector<std::shared_ptr<minikin::FontFamily>>(
+ {buildFamily(kRobotoVariable), buildFamily(kJPFont)}),
+ RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE, nullptr /* fallback */);
+}
+
+// Execute a text layout.
+minikin::Layout doLayout(const std::vector<uint16_t> text, Paint paint, Typeface* typeface) {
+ return MinikinUtils::doLayout(&paint, minikin::Bidi::LTR, typeface, text.data(), text.size(),
+ 0 /* start */, text.size(), 0, text.size(), nullptr);
+}
+
+DrawTextFunctor processFunctor(const std::vector<uint16_t>& text, Paint* paint) {
+ // Create canvas
+ SkImageInfo info = SkImageInfo::Make(1, 1, kN32_SkColorType, kOpaque_SkAlphaType);
+ sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(info);
+ SkBitmap skBitmap;
+ bitmap->getSkBitmap(&skBitmap);
+ SkiaCanvas canvas(skBitmap);
+
+ // Create minikin::Layout
+ std::unique_ptr<Typeface> typeface(makeTypeface());
+ minikin::Layout layout = doLayout(text, *paint, typeface.get());
+
+ DrawTextFunctor f(layout, &canvas, *paint, 0, 0, layout.getAdvance());
+ MinikinUtils::forFontRun(layout, paint, f);
+ return f;
+}
+
+TEST_WITH_FLAGS(UnderlineTest, Roboto,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::text::flags,
+ fix_double_underline))) {
+ float textSize = 100;
+ Paint paint;
+ paint.getSkFont().setSize(textSize);
+ paint.setUnderline(true);
+ // the text is "abc"
+ DrawTextFunctor functor = processFunctor({0x0061, 0x0062, 0x0063}, &paint);
+
+ EXPECT_EQ(ROBOTO_POSITION_EM * textSize, functor.getUnderlinePosition());
+ EXPECT_EQ(ROBOTO_THICKNESS_EM * textSize, functor.getUnderlineThickness());
+}
+
+TEST_WITH_FLAGS(UnderlineTest, NotoCJK,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::text::flags,
+ fix_double_underline))) {
+ float textSize = 100;
+ Paint paint;
+ paint.getSkFont().setSize(textSize);
+ paint.setUnderline(true);
+ // The text is あいう in Japanese
+ DrawTextFunctor functor = processFunctor({0x3042, 0x3044, 0x3046}, &paint);
+
+ EXPECT_EQ(NOTO_CJK_POSITION_EM * textSize, functor.getUnderlinePosition());
+ EXPECT_EQ(NOTO_CJK_THICKNESS_EM * textSize, functor.getUnderlineThickness());
+}
+
+TEST_WITH_FLAGS(UnderlineTest, Mixture,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::text::flags,
+ fix_double_underline))) {
+ float textSize = 100;
+ Paint paint;
+ paint.getSkFont().setSize(textSize);
+ paint.setUnderline(true);
+ // The text is aいc. The only middle of the character is Japanese.
+ DrawTextFunctor functor = processFunctor({0x0061, 0x3044, 0x0063}, &paint);
+
+ // We use the bottom, thicker line as underline. Here, use Noto's one.
+ EXPECT_EQ(NOTO_CJK_POSITION_EM * textSize, functor.getUnderlinePosition());
+ EXPECT_EQ(NOTO_CJK_THICKNESS_EM * textSize, functor.getUnderlineThickness());
+}
+} // namespace
diff --git a/location/Android.bp b/location/Android.bp
index cfe0e49..eb7cd01 100644
--- a/location/Android.bp
+++ b/location/Android.bp
@@ -39,3 +39,8 @@
],
},
}
+
+platform_compat_config {
+ name: "framework-location-compat-config",
+ src: ":framework-location",
+}
diff --git a/media/java/android/media/projection/TEST_MAPPING b/media/java/android/media/projection/TEST_MAPPING
index a792498..7aa9118 100644
--- a/media/java/android/media/projection/TEST_MAPPING
+++ b/media/java/android/media/projection/TEST_MAPPING
@@ -4,9 +4,6 @@
"name": "MediaProjectionTests",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
- },
- {
"exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index a4e5fb6..196b5c3 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -401,7 +401,9 @@
private void resetInternal() {
mSessionCallback = null;
- mPendingAppPrivateCommands.clear();
+ synchronized (mPendingAppPrivateCommands) {
+ mPendingAppPrivateCommands.clear();
+ }
if (mSession != null) {
setSessionSurface(null);
removeSessionOverlayView();
@@ -691,7 +693,10 @@
} else {
Log.w(TAG, "sendAppPrivateCommand - session not yet created (action \"" + action
+ "\" pending)");
- mPendingAppPrivateCommands.add(Pair.create(action, data));
+
+ synchronized (mPendingAppPrivateCommands) {
+ mPendingAppPrivateCommands.add(Pair.create(action, data));
+ }
}
}
@@ -1320,10 +1325,13 @@
mSession = session;
if (session != null) {
// Sends the pending app private commands first.
- for (Pair<String, Bundle> command : mPendingAppPrivateCommands) {
- mSession.sendAppPrivateCommand(command.first, command.second);
+
+ synchronized (mPendingAppPrivateCommands) {
+ for (Pair<String, Bundle> command : mPendingAppPrivateCommands) {
+ mSession.sendAppPrivateCommand(command.first, command.second);
+ }
+ mPendingAppPrivateCommands.clear();
}
- mPendingAppPrivateCommands.clear();
synchronized (sMainTvViewLock) {
if (hasWindowFocus() && TvView.this == sMainTvView.get()
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index f9ea773..49bd9d9 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -345,6 +345,8 @@
<string name="accessibility_wifi_three_bars">Wifi three bars.</string>
<!-- Content description of the WIFI signal when it is full for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_wifi_signal_full">Wifi signal full.</string>
+ <!-- Content description of the WIFI signal when the WIFI is connected using the signal from a different device owned by the user. For accessibility (not shown on the screen) [CHAR LIMIT=NONE] -->
+ <string name="accessibility_wifi_other_device">Connected to your device.</string>
<!-- Content description of the Wi-Fi security type. This message indicates this is an open Wi-Fi (no password needed) [CHAR LIMIT=NONE] -->
<string name="accessibility_wifi_security_type_none">Open network</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java b/packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java
index ee65ef4..ce466df 100644
--- a/packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java
+++ b/packages/SettingsLib/src/com/android/settingslib/AccessibilityContentDescriptions.java
@@ -50,6 +50,7 @@
};
public static final int WIFI_NO_CONNECTION = R.string.accessibility_no_wifi;
+ public static final int WIFI_OTHER_DEVICE_CONNECTION = R.string.accessibility_wifi_other_device;
public static final int NO_CALLING = R.string.accessibility_no_calling;
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 5c2f979..0623d4a 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -69,7 +69,7 @@
"exclude-annotation": "org.junit.Ignore"
},
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
"include-filter": "android.permissionui.cts.CameraMicIndicatorsPermissionTest"
diff --git a/packages/SystemUI/compose/core/Android.bp b/packages/SystemUI/compose/core/Android.bp
index 510fa1e..42d088f 100644
--- a/packages/SystemUI/compose/core/Android.bp
+++ b/packages/SystemUI/compose/core/Android.bp
@@ -34,7 +34,9 @@
"androidx.compose.runtime_runtime",
"androidx.compose.material3_material3",
+ "androidx.compose.material3_material3-window-size-class",
"androidx.savedstate_savedstate",
+ "androidx.window_window",
],
kotlincflags: ["-Xjvm-default=all"],
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/theme/PlatformTheme.kt b/packages/SystemUI/compose/core/src/com/android/compose/theme/PlatformTheme.kt
index b4e90d6..0661870 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/theme/PlatformTheme.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/theme/PlatformTheme.kt
@@ -29,6 +29,8 @@
import com.android.compose.theme.typography.TypefaceTokens
import com.android.compose.theme.typography.TypographyTokens
import com.android.compose.theme.typography.platformTypography
+import com.android.compose.windowsizeclass.LocalWindowSizeClass
+import com.android.compose.windowsizeclass.calculateWindowSizeClass
/** The Material 3 theme that should wrap all Platform Composables. */
@Composable
@@ -51,10 +53,12 @@
remember(typefaceNames) {
platformTypography(TypographyTokens(TypeScaleTokens(TypefaceTokens(typefaceNames))))
}
+ val windowSizeClass = calculateWindowSizeClass()
MaterialTheme(colorScheme, typography = typography) {
CompositionLocalProvider(
LocalAndroidColorScheme provides androidColorScheme,
+ LocalWindowSizeClass provides windowSizeClass,
) {
content()
}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/windowsizeclass/WindowSizeClass.kt b/packages/SystemUI/compose/core/src/com/android/compose/windowsizeclass/WindowSizeClass.kt
new file mode 100644
index 0000000..4674d6e
--- /dev/null
+++ b/packages/SystemUI/compose/core/src/com/android/compose/windowsizeclass/WindowSizeClass.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.windowsizeclass
+
+import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
+import androidx.compose.material3.windowsizeclass.WindowSizeClass
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.staticCompositionLocalOf
+import androidx.compose.ui.graphics.toComposeRect
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalDensity
+import androidx.window.layout.WindowMetricsCalculator
+
+val LocalWindowSizeClass =
+ staticCompositionLocalOf<WindowSizeClass> {
+ throw IllegalStateException(
+ "No WindowSizeClass configured. Make sure to use LocalWindowSizeClass in a Composable" +
+ " surrounded by a PlatformTheme {}."
+ )
+ }
+
+@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
+@Composable
+fun calculateWindowSizeClass(): WindowSizeClass {
+ // Observe view configuration changes and recalculate the size class on each change.
+ LocalConfiguration.current
+ val density = LocalDensity.current
+ val context = LocalContext.current
+ val metrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(context)
+ val size = with(density) { metrics.bounds.toComposeRect().size.toDpSize() }
+ return WindowSizeClass.calculateFromSize(size)
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
index d0f2ce8..a61e959 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
@@ -14,40 +14,49 @@
* limitations under the License.
*/
-@file:OptIn(ExperimentalMaterial3Api::class)
-
package com.android.systemui.bouncer.ui.composable
import android.app.AlertDialog
import android.app.Dialog
import android.content.DialogInterface
import androidx.compose.animation.Crossfade
+import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.snap
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
-import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
+import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.SceneScope
+import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.systemui.bouncer.ui.viewmodel.AuthMethodBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
@@ -103,96 +112,231 @@
dialogFactory: BouncerSceneDialogFactory,
modifier: Modifier = Modifier,
) {
- val message: BouncerViewModel.MessageViewModel by viewModel.message.collectAsState()
- val authMethodViewModel: AuthMethodBouncerViewModel? by
- viewModel.authMethodViewModel.collectAsState()
- val dialogMessage: String? by viewModel.throttlingDialogMessage.collectAsState()
- var dialog: Dialog? by remember { mutableStateOf(null) }
val backgroundColor = MaterialTheme.colorScheme.surface
+ val windowSizeClass = LocalWindowSizeClass.current
Box(modifier) {
Canvas(Modifier.element(Bouncer.Elements.Background).fillMaxSize()) {
drawRect(color = backgroundColor)
}
- Column(
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.spacedBy(60.dp),
- modifier =
- Modifier.element(Bouncer.Elements.Content)
- .fillMaxSize()
- .padding(start = 32.dp, top = 92.dp, end = 32.dp, bottom = 32.dp)
- ) {
- Crossfade(
- targetState = message,
- label = "Bouncer message",
- animationSpec = if (message.isUpdateAnimated) tween() else snap(),
- ) { message ->
- Text(
- text = message.text,
- color = MaterialTheme.colorScheme.onSurface,
- style = MaterialTheme.typography.bodyLarge,
+ val childModifier = Modifier.element(Bouncer.Elements.Content).fillMaxSize()
+
+ when (windowSizeClass.widthSizeClass) {
+ WindowWidthSizeClass.Expanded ->
+ SideBySide(
+ viewModel = viewModel,
+ dialogFactory = dialogFactory,
+ modifier = childModifier,
)
- }
-
- Box(Modifier.weight(1f)) {
- when (val nonNullViewModel = authMethodViewModel) {
- is PinBouncerViewModel ->
- PinBouncer(
- viewModel = nonNullViewModel,
- modifier = Modifier.align(Alignment.Center),
- )
- is PasswordBouncerViewModel ->
- PasswordBouncer(
- viewModel = nonNullViewModel,
- modifier = Modifier.align(Alignment.Center),
- )
- is PatternBouncerViewModel ->
- PatternBouncer(
- viewModel = nonNullViewModel,
- modifier =
- Modifier.aspectRatio(1f, matchHeightConstraintsFirst = false)
- .align(Alignment.BottomCenter),
- )
- else -> Unit
- }
- }
-
- Button(
- onClick = viewModel::onEmergencyServicesButtonClicked,
- colors =
- ButtonDefaults.buttonColors(
- containerColor = MaterialTheme.colorScheme.tertiaryContainer,
- contentColor = MaterialTheme.colorScheme.onTertiaryContainer,
- ),
- ) {
- Text(
- text = stringResource(com.android.internal.R.string.lockscreen_emergency_call),
- style = MaterialTheme.typography.bodyMedium,
+ WindowWidthSizeClass.Medium ->
+ Stacked(
+ viewModel = viewModel,
+ dialogFactory = dialogFactory,
+ modifier = childModifier,
)
- }
+ else ->
+ Bouncer(
+ viewModel = viewModel,
+ dialogFactory = dialogFactory,
+ modifier = childModifier,
+ )
+ }
+ }
+}
- if (dialogMessage != null) {
- if (dialog == null) {
- dialog =
- dialogFactory().apply {
- setMessage(dialogMessage)
- setButton(
- DialogInterface.BUTTON_NEUTRAL,
- context.getString(R.string.ok),
- ) { _, _ ->
- viewModel.onThrottlingDialogDismissed()
- }
- setCancelable(false)
- setCanceledOnTouchOutside(false)
- show()
- }
- }
- } else {
- dialog?.dismiss()
- dialog = null
+/**
+ * Renders the contents of the actual bouncer UI, the area that takes user input to do an
+ * authentication attempt, including all messaging UI (directives, reasoning, errors, etc.).
+ */
+@Composable
+private fun Bouncer(
+ viewModel: BouncerViewModel,
+ dialogFactory: BouncerSceneDialogFactory,
+ modifier: Modifier = Modifier,
+) {
+ val message: BouncerViewModel.MessageViewModel by viewModel.message.collectAsState()
+ val authMethodViewModel: AuthMethodBouncerViewModel? by
+ viewModel.authMethodViewModel.collectAsState()
+ val dialogMessage: String? by viewModel.throttlingDialogMessage.collectAsState()
+ var dialog: Dialog? by remember { mutableStateOf(null) }
+
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(60.dp),
+ modifier = modifier.padding(start = 32.dp, top = 92.dp, end = 32.dp, bottom = 32.dp)
+ ) {
+ Crossfade(
+ targetState = message,
+ label = "Bouncer message",
+ animationSpec = if (message.isUpdateAnimated) tween() else snap(),
+ ) { message ->
+ Text(
+ text = message.text,
+ color = MaterialTheme.colorScheme.onSurface,
+ style = MaterialTheme.typography.bodyLarge,
+ )
+ }
+
+ Box(Modifier.weight(1f)) {
+ when (val nonNullViewModel = authMethodViewModel) {
+ is PinBouncerViewModel ->
+ PinBouncer(
+ viewModel = nonNullViewModel,
+ modifier = Modifier.align(Alignment.Center),
+ )
+ is PasswordBouncerViewModel ->
+ PasswordBouncer(
+ viewModel = nonNullViewModel,
+ modifier = Modifier.align(Alignment.Center),
+ )
+ is PatternBouncerViewModel ->
+ PatternBouncer(
+ viewModel = nonNullViewModel,
+ modifier =
+ Modifier.aspectRatio(1f, matchHeightConstraintsFirst = false)
+ .align(Alignment.BottomCenter),
+ )
+ else -> Unit
}
}
+
+ Button(
+ onClick = viewModel::onEmergencyServicesButtonClicked,
+ colors =
+ ButtonDefaults.buttonColors(
+ containerColor = MaterialTheme.colorScheme.tertiaryContainer,
+ contentColor = MaterialTheme.colorScheme.onTertiaryContainer,
+ ),
+ ) {
+ Text(
+ text = stringResource(com.android.internal.R.string.lockscreen_emergency_call),
+ style = MaterialTheme.typography.bodyMedium,
+ )
+ }
+
+ if (dialogMessage != null) {
+ if (dialog == null) {
+ dialog =
+ dialogFactory().apply {
+ setMessage(dialogMessage)
+ setButton(
+ DialogInterface.BUTTON_NEUTRAL,
+ context.getString(R.string.ok),
+ ) { _, _ ->
+ viewModel.onThrottlingDialogDismissed()
+ }
+ setCancelable(false)
+ setCanceledOnTouchOutside(false)
+ show()
+ }
+ }
+ } else {
+ dialog?.dismiss()
+ dialog = null
+ }
+ }
+}
+
+/** Renders the UI of the user switcher that's displayed on large screens next to the bouncer UI. */
+@Composable
+private fun UserSwitcher(
+ modifier: Modifier = Modifier,
+) {
+ Box(modifier) {
+ Text(
+ text = "TODO: the user switcher goes here",
+ modifier = Modifier.align(Alignment.Center)
+ )
+ }
+}
+
+/**
+ * Arranges the bouncer contents and user switcher contents side-by-side, supporting a double tap
+ * anywhere on the background to flip their positions.
+ */
+@Composable
+private fun SideBySide(
+ viewModel: BouncerViewModel,
+ dialogFactory: BouncerSceneDialogFactory,
+ modifier: Modifier = Modifier,
+) {
+ val layoutDirection = LocalLayoutDirection.current
+ val isLeftToRight = layoutDirection == LayoutDirection.Ltr
+ val (isUserSwitcherFirst, setUserSwitcherFirst) =
+ rememberSaveable(isLeftToRight) { mutableStateOf(isLeftToRight) }
+
+ Row(
+ modifier =
+ modifier.pointerInput(Unit) {
+ detectTapGestures(
+ onDoubleTap = { offset ->
+ // Depending on where the user double tapped, switch the elements such that
+ // the bouncer contents element is closer to the side that was double
+ // tapped.
+ setUserSwitcherFirst(offset.x > size.width / 2)
+ }
+ )
+ },
+ ) {
+ val animatedOffset by
+ animateFloatAsState(
+ targetValue =
+ if (isUserSwitcherFirst) {
+ // When the user switcher is first, both elements have their natural
+ // placement so they are not offset in any way.
+ 0f
+ } else if (isLeftToRight) {
+ // Since the user switcher is not first, the elements have to be swapped
+ // horizontally. In the case of LTR locales, this means pushing the user
+ // switcher to the right, hence the positive number.
+ 1f
+ } else {
+ // Since the user switcher is not first, the elements have to be swapped
+ // horizontally. In the case of RTL locales, this means pushing the user
+ // switcher to the left, hence the negative number.
+ -1f
+ },
+ label = "offset",
+ )
+
+ UserSwitcher(
+ modifier =
+ Modifier.fillMaxHeight().weight(1f).graphicsLayer {
+ translationX = size.width * animatedOffset
+ },
+ )
+ Bouncer(
+ viewModel = viewModel,
+ dialogFactory = dialogFactory,
+ modifier =
+ Modifier.fillMaxHeight().weight(1f).graphicsLayer {
+ // A negative sign is used to make sure this is offset in the direction that's
+ // opposite of the direction that the user switcher is pushed in.
+ translationX = -size.width * animatedOffset
+ },
+ )
+ }
+}
+
+/** Arranges the bouncer contents and user switcher contents one on top of the other. */
+@Composable
+private fun Stacked(
+ viewModel: BouncerViewModel,
+ dialogFactory: BouncerSceneDialogFactory,
+ modifier: Modifier = Modifier,
+) {
+ Column(
+ modifier = modifier,
+ ) {
+ UserSwitcher(
+ modifier = Modifier.fillMaxWidth().weight(1f),
+ )
+ Bouncer(
+ viewModel = viewModel,
+ dialogFactory = dialogFactory,
+ modifier = Modifier.fillMaxWidth().weight(1f),
+ )
}
}
diff --git a/packages/SystemUI/res/layout/connected_display_dialog.xml b/packages/SystemUI/res/layout/connected_display_dialog.xml
index 569dd4c..a51c55e 100644
--- a/packages/SystemUI/res/layout/connected_display_dialog.xml
+++ b/packages/SystemUI/res/layout/connected_display_dialog.xml
@@ -52,7 +52,7 @@
style="@style/Widget.Dialog.Button.BorderButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@string/cancel" />
+ android:text="@string/dismiss_dialog" />
<Space
android:layout_width="0dp"
@@ -64,6 +64,6 @@
style="@style/Widget.Dialog.Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@string/enable_display" />
+ android:text="@string/mirror_display" />
</LinearLayout>
</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-land/config.xml b/packages/SystemUI/res/values-land/config.xml
index 85fb3ac..587caaf 100644
--- a/packages/SystemUI/res/values-land/config.xml
+++ b/packages/SystemUI/res/values-land/config.xml
@@ -44,6 +44,6 @@
<!-- Whether to force split shade.
For now, this value has effect only when flag lockscreen.enable_landscape is enabled.
- TODO (b/293290851) - change this comment/resource when flag is enabled -->
+ TODO (b/293252410) - change this comment/resource when flag is enabled -->
<bool name="force_config_use_split_notification_shade">true</bool>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml
index e63229a..fc6d20e 100644
--- a/packages/SystemUI/res/values-sw600dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/config.xml
@@ -38,6 +38,6 @@
<!-- Whether to force split shade.
For now, this value has effect only when flag lockscreen.enable_landscape is enabled.
- TODO (b/293290851) - change this comment/resource when flag is enabled -->
+ TODO (b/293252410) - change this comment/resource when flag is enabled -->
<bool name="force_config_use_split_notification_shade">false</bool>
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index b6bca65..18f24ec 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -88,7 +88,7 @@
<!-- The default tiles to display in QuickSettings -->
<string name="quick_settings_tiles_default" translatable="false">
- internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,custom(com.android.permissioncontroller/.permission.service.SafetyCenterQsTileService)
+ internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,custom(com.android.permissioncontroller/.permission.service.v33.SafetyCenterQsTileService)
</string>
<!-- The class path of the Safety Quick Settings Tile -->
@@ -604,7 +604,7 @@
<!-- Whether to force split shade.
For now, this value has effect only when flag lockscreen.enable_landscape is enabled.
- TODO (b/293290851) - change this comment/resource when flag is enabled -->
+ TODO (b/293252410) - change this comment/resource when flag is enabled -->
<bool name="force_config_use_split_notification_shade">false</bool>
<!-- Whether we use large screen shade header which takes only one row compared to QS header -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 5860806..a2637d5 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3214,9 +3214,10 @@
<!--- Title of the dialog appearing when an external display is connected, asking whether to start mirroring [CHAR LIMIT=NONE]-->
<string name="connected_display_dialog_start_mirroring">Mirror to external display?</string>
-
<!--- Label of the "enable display" button of the dialog appearing when an external display is connected [CHAR LIMIT=NONE]-->
- <string name="enable_display">Enable display</string>
+ <string name="mirror_display">Mirror display</string>
+ <!--- Label of the dismiss button of the dialog appearing when an external display is connected [CHAR LIMIT=NONE]-->
+ <string name="dismiss_dialog">Dismiss</string>
<!-- Title of the privacy dialog, shown for active / recent app usage of some phone sensors [CHAR LIMIT=30] -->
<string name="privacy_dialog_title">Microphone & Camera</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 165c4bb..a81069a 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -424,11 +424,11 @@
private void updateConfiguration() {
WindowManager windowManager = mContext.getSystemService(WindowManager.class);
Rect bounds = windowManager.getCurrentWindowMetrics().getBounds();
- WindowInsets insets = windowManager.getCurrentWindowMetrics().getWindowInsets();
mWidthPixels = bounds.right;
if (mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)) {
// Assumed to be initially neglected as there are no left or right insets in portrait
// However, on landscape, these insets need to included when calculating the midpoint
+ WindowInsets insets = windowManager.getCurrentWindowMetrics().getWindowInsets();
mWidthPixels -= insets.getSystemWindowInsetLeft() + insets.getSystemWindowInsetRight();
}
mHeightPixels = bounds.bottom;
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
index 9b93522..e8a8444 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
@@ -18,26 +18,25 @@
import android.app.Activity
import android.app.ActivityOptions
-import android.app.ActivityTaskManager
-import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
import android.app.Dialog
import android.app.PendingIntent
import android.content.ComponentName
import android.content.Context
import android.content.Intent
-import android.graphics.Rect
import android.view.View
import android.view.ViewGroup
import android.view.WindowInsets
import android.view.WindowInsets.Type
import android.view.WindowManager
import android.widget.ImageView
+import androidx.annotation.VisibleForTesting
import com.android.internal.policy.ScreenDecorationsUtils
import com.android.systemui.res.R
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.boundsOnScreen
import com.android.wm.shell.taskview.TaskView
/**
@@ -65,8 +64,8 @@
private const val EXTRA_USE_PANEL = "controls.DISPLAY_IN_PANEL"
}
- var detailTaskId = INVALID_TASK_ID
private lateinit var taskViewContainer: View
+ private lateinit var controlDetailRoot: View
private val taskWidthPercentWidth = activityContext.resources.getFloat(
R.dimen.controls_task_view_width_percentage
)
@@ -79,12 +78,7 @@
addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
}
- fun removeDetailTask() {
- if (detailTaskId == INVALID_TASK_ID) return
- ActivityTaskManager.getInstance().removeTask(detailTaskId)
- detailTaskId = INVALID_TASK_ID
- }
-
+ @VisibleForTesting
val stateCallback = object : TaskView.Listener {
override fun onInitialized() {
taskViewContainer.apply {
@@ -98,33 +92,29 @@
activityContext,
0 /* enterResId */,
0 /* exitResId */
- ).setPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
- options.isPendingIntentBackgroundActivityLaunchAllowedByPermission = true
+ ).apply {
+ pendingIntentBackgroundActivityStartMode = MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+ isPendingIntentBackgroundActivityLaunchAllowedByPermission = true
+ taskAlwaysOnTop = true
+ }
taskView.startActivity(
pendingIntent,
fillInIntent,
options,
- getTaskViewBounds()
+ taskView.boundsOnScreen,
)
}
override fun onTaskRemovalStarted(taskId: Int) {
- detailTaskId = INVALID_TASK_ID
- dismiss()
+ taskView.release()
}
override fun onTaskCreated(taskId: Int, name: ComponentName?) {
- detailTaskId = taskId
requireViewById<ViewGroup>(R.id.controls_activity_view).apply {
setAlpha(1f)
}
}
-
- override fun onReleased() {
- removeDetailTask()
- }
-
override fun onBackPressedOnTaskRoot(taskId: Int) {
dismiss()
}
@@ -138,6 +128,9 @@
setContentView(R.layout.controls_detail_dialog)
taskViewContainer = requireViewById<ViewGroup>(R.id.control_task_view_container)
+ controlDetailRoot = requireViewById<View>(R.id.control_detail_root).apply {
+ setOnClickListener { _: View -> dismiss() }
+ }
requireViewById<ViewGroup>(R.id.controls_activity_view).apply {
addView(taskView)
@@ -147,13 +140,9 @@
requireViewById<ImageView>(R.id.control_detail_close).apply {
setOnClickListener { _: View -> dismiss() }
}
- requireViewById<View>(R.id.control_detail_root).apply {
- setOnClickListener { _: View -> dismiss() }
- }
requireViewById<ImageView>(R.id.control_detail_open_in_app).apply {
setOnClickListener { v: View ->
- removeDetailTask()
dismiss()
val action = ActivityStarter.OnDismissAction {
@@ -201,26 +190,9 @@
taskView.setListener(cvh.uiExecutor, stateCallback)
}
- fun getTaskViewBounds(): Rect {
- val wm = checkNotNull(context.getSystemService(WindowManager::class.java))
- val windowMetrics = wm.getCurrentWindowMetrics()
- val rect = windowMetrics.bounds
- val metricInsets = windowMetrics.windowInsets
- val insets = metricInsets.getInsetsIgnoringVisibility(Type.systemBars()
- or Type.displayCutout())
- val headerHeight = context.resources.getDimensionPixelSize(
- R.dimen.controls_detail_dialog_header_height)
-
- val finalRect = Rect(rect.left - insets.left /* left */,
- rect.top + insets.top + headerHeight /* top */,
- rect.right - insets.right /* right */,
- rect.bottom - insets.bottom /* bottom */)
- return finalRect
- }
-
override fun dismiss() {
if (!isShowing()) return
- taskView.release()
+ taskView.removeTask()
val isActivityFinishing =
(activityContext as? Activity)?.let { it.isFinishing || it.isDestroyed }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt
index 1b0d032..848c786 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt
@@ -18,7 +18,6 @@
package com.android.systemui.controls.ui
import android.app.ActivityOptions
-import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.app.PendingIntent
import android.content.ComponentName
import android.content.Context
@@ -45,8 +44,6 @@
taskView.alpha = 0f
}
- private var detailTaskId = INVALID_TASK_ID
-
private val fillInIntent =
Intent().apply {
// Apply flags to make behaviour match documentLaunchMode=always.
@@ -57,7 +54,6 @@
private val stateCallback =
object : TaskView.Listener {
override fun onInitialized() {
-
val options =
ActivityOptions.makeCustomAnimation(
activityContext,
@@ -88,12 +84,10 @@
}
override fun onTaskRemovalStarted(taskId: Int) {
- detailTaskId = INVALID_TASK_ID
release()
}
override fun onTaskCreated(taskId: Int, name: ComponentName?) {
- detailTaskId = taskId
taskView.alpha = 1f
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 3b70555..10f1d7c 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -80,12 +80,6 @@
@JvmField
val NOTIFICATION_INLINE_REPLY_ANIMATION = releasedFlag("notification_inline_reply_animation")
- /** Makes sure notification panel is updated before the user switch is complete. */
- // TODO(b/278873737): Tracking Bug
- @JvmField
- val LOAD_NOTIFICATIONS_BEFORE_THE_USER_SWITCH_IS_COMPLETE =
- releasedFlag("load_notifications_before_the_user_switch_is_complete")
-
// TODO(b/277338665): Tracking Bug
@JvmField
val NOTIFICATION_SHELF_REFACTOR =
@@ -303,6 +297,11 @@
@JvmField val MIGRATE_CLOCKS_TO_BLUEPRINT =
unreleasedFlag("migrate_clocks_to_blueprint")
+ /** Migrate KeyguardRootView to use composables. */
+ // TODO(b/301969856): Tracking Bug.
+ @JvmField val KEYGUARD_ROOT_VIEW_USE_COMPOSE =
+ unreleasedFlag("keyguard_root_view_use_compose")
+
/** Enables preview loading animation in the wallpaper picker. */
// TODO(b/274443705): Tracking Bug
@JvmField
@@ -771,6 +770,10 @@
// TODO(b/302087895): Tracking Bug
@JvmField val CALL_LAYOUT_ASYNC_SET_DATA = unreleasedFlag("call_layout_async_set_data")
+ // TODO(b/302144438): Tracking Bug
+ @JvmField val DECOUPLE_REMOTE_INPUT_DELEGATE_AND_CALLBACK_UPDATE =
+ unreleasedFlag("decouple_remote_input_delegate_and_callback_update")
+
// 2900 - CentralSurfaces-related flags
// TODO(b/285174336): Tracking Bug
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index fb02c7d..1761ca8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -26,7 +26,6 @@
import com.android.keyguard.LockIconViewController
import com.android.keyguard.dagger.KeyguardStatusViewComponent
import com.android.systemui.CoreStartable
-import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
@@ -40,7 +39,9 @@
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
+import com.android.systemui.res.R
import com.android.systemui.shade.NotificationShadeWindowView
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.KeyguardIndicationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
@@ -69,6 +70,7 @@
private val context: Context,
private val keyguardIndicationController: KeyguardIndicationController,
private val lockIconViewController: LockIconViewController,
+ private val shadeInteractor: ShadeInteractor,
) : CoreStartable {
private var rootViewHandle: DisposableHandle? = null
@@ -135,6 +137,7 @@
occludingAppDeviceEntryMessageViewModel,
chipbarCoordinator,
keyguardStateController,
+ shadeInteractor
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index f564d00..053727a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -32,6 +32,7 @@
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.temporarydisplay.ViewPriority
@@ -55,6 +56,7 @@
occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel,
chipbarCoordinator: ChipbarCoordinator,
keyguardStateController: KeyguardStateController,
+ shadeInteractor: ShadeInteractor,
): DisposableHandle {
val disposableHandle =
view.repeatWhenAttached {
@@ -88,6 +90,17 @@
}
}
}
+
+ launch {
+ shadeInteractor.isAnyFullyExpanded.collect { isFullyAnyExpanded ->
+ view.visibility =
+ if (isFullyAnyExpanded) {
+ View.INVISIBLE
+ } else {
+ View.VISIBLE
+ }
+ }
+ }
}
repeatOnLifecycle(Lifecycle.State.STARTED) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index 2ad74fb..864e345 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -62,6 +62,7 @@
import com.android.systemui.monet.ColorScheme
import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shared.clocks.ClockRegistry
import com.android.systemui.shared.clocks.DefaultClockController
import com.android.systemui.shared.clocks.shared.model.ClockPreviewConstants
@@ -109,6 +110,7 @@
private val occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel,
private val chipbarCoordinator: ChipbarCoordinator,
private val keyguardStateController: KeyguardStateController,
+ private val shadeInteractor: ShadeInteractor,
) {
val hostToken: IBinder? = bundle.getBinder(KEY_HOST_TOKEN)
@@ -317,6 +319,7 @@
occludingAppDeviceEntryMessageViewModel,
chipbarCoordinator,
keyguardStateController,
+ shadeInteractor,
)
)
rootView.addView(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt
index 9409036..f4bc713 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt
@@ -29,11 +29,11 @@
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.LockIconView
import com.android.keyguard.LockIconViewController
-import com.android.systemui.res.R
import com.android.systemui.biometrics.AuthController
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
import javax.inject.Inject
@@ -73,11 +73,11 @@
val mBottomPaddingPx =
context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom)
val bounds = windowManager.currentWindowMetrics.bounds
- val insets = windowManager.currentWindowMetrics.windowInsets
var widthPixels = bounds.right.toFloat()
if (featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)) {
// Assumed to be initially neglected as there are no left or right insets in portrait.
// However, on landscape, these insets need to included when calculating the midpoint.
+ val insets = windowManager.currentWindowMetrics.windowInsets
widthPixels -= (insets.systemWindowInsetLeft + insets.systemWindowInsetRight).toFloat()
}
val heightPixels = bounds.bottom.toFloat()
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
index ae3c912..ed6d41e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
@@ -101,7 +101,8 @@
panelEventsEvents: ShadeStateEvents,
private val secureSettings: SecureSettings,
@Main private val handler: Handler,
- private val splitShadeStateController: SplitShadeStateController
+ private val splitShadeStateController: SplitShadeStateController,
+ private val logger: MediaViewLogger,
) {
/** Track the media player setting status on lock screen. */
@@ -1057,6 +1058,7 @@
// that and directly set the mediaFrame's bounds within the premeasured host.
targetHost.addView(mediaFrame)
}
+ logger.logMediaHostAttachment(currentAttachmentLocation)
if (isCrossFadeAnimatorRunning) {
// When cross-fading with an animation, we only notify the media carousel of the
// location change, once the view is reattached to the new place and not
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewLogger.kt
index 8f1595d..3ff23159 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewLogger.kt
@@ -52,4 +52,8 @@
{ "location ($str1): $int1 -> $int2" }
)
}
+
+ fun logMediaHostAttachment(host: Int) {
+ buffer.log(TAG, LogLevel.DEBUG, { int1 = host }, { "Host (updateHostAttachment): $int1" })
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 9a9626d..10f95e0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -48,6 +48,7 @@
import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository;
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository;
+import com.android.systemui.qs.tiles.di.NewQSTileFactory;
import com.android.systemui.settings.UserFileManager;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeController;
@@ -56,6 +57,8 @@
import com.android.systemui.tuner.TunerService.Tunable;
import com.android.systemui.util.settings.SecureSettings;
+import dagger.Lazy;
+
import org.jetbrains.annotations.NotNull;
import java.io.PrintWriter;
@@ -121,6 +124,7 @@
@Inject
public QSTileHost(Context context,
+ Lazy<NewQSTileFactory> newQsTileFactoryProvider,
QSFactory defaultFactory,
@Main Executor mainExecutor,
PluginManager pluginManager,
@@ -147,6 +151,9 @@
mShadeController = shadeController;
+ if (featureFlags.getPipelineTilesEnabled()) {
+ mQsFactories.add(newQsTileFactoryProvider.get());
+ }
mQsFactories.add(defaultFactory);
pluginManager.addPluginListener(this, QSFactory.class, true);
mUserTracker = userTracker;
@@ -326,7 +333,6 @@
try {
tile = createTile(tileSpec);
if (tile != null) {
- tile.setTileSpec(tileSpec);
if (tile.isAvailable()) {
newTiles.put(tileSpec, tile);
mQSLogger.logTileAdded(tileSpec);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index a6226b3..2af7ae0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -130,11 +130,9 @@
if (tile == null) {
continue;
} else if (!tile.isAvailable()) {
- tile.setTileSpec(spec);
tile.destroy();
continue;
}
- tile.setTileSpec(spec);
tilesToAdd.add(tile);
}
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 92490e8..a65967a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -31,6 +31,7 @@
import com.android.systemui.qs.external.QSExternalModule;
import com.android.systemui.qs.pipeline.dagger.QSPipelineModule;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel;
import com.android.systemui.statusbar.phone.AutoTileManager;
import com.android.systemui.statusbar.phone.ManagedProfileController;
import com.android.systemui.statusbar.policy.CastController;
@@ -41,14 +42,14 @@
import com.android.systemui.statusbar.policy.WalletController;
import com.android.systemui.util.settings.SecureSettings;
-import java.util.Map;
-
-import javax.inject.Named;
-
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.Multibinds;
+import java.util.Map;
+
+import javax.inject.Named;
+
/**
* Module for QS dependencies
*/
@@ -68,6 +69,11 @@
@Multibinds
Map<String, QSTileImpl<?>> tileMap();
+ /** A map of internal QS tile ViewModels. Ensures that this can be injected even if
+ * it is empty */
+ @Multibinds
+ Map<String, QSTileViewModel> tileViewModelMap();
+
@Provides
@SysUISingleton
static AutoTileManager provideAutoTileManager(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
index 00c2358..c5512c1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
@@ -41,10 +41,12 @@
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.android.systemui.qs.tiles.di.NewQSTileFactory
import com.android.systemui.qs.toProto
import com.android.systemui.settings.UserTracker
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.kotlin.pairwise
+import dagger.Lazy
import java.io.PrintWriter
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -131,6 +133,7 @@
private val installedTilesComponentRepository: InstalledTilesComponentRepository,
private val userRepository: UserRepository,
private val customTileStatePersister: CustomTileStatePersister,
+ private val newQSTileFactory: Lazy<NewQSTileFactory>,
private val tileFactory: QSFactory,
private val customTileAddedRepository: CustomTileAddedRepository,
private val tileLifecycleManagerFactory: TileLifecycleManager.Factory,
@@ -139,7 +142,7 @@
@Background private val backgroundDispatcher: CoroutineDispatcher,
@Application private val scope: CoroutineScope,
private val logger: QSPipelineLogger,
- featureFlags: QSPipelineFlagsRepository,
+ private val featureFlags: QSPipelineFlagsRepository,
) : CurrentTilesInteractor {
private val _currentSpecsAndTiles: MutableStateFlow<List<TileModel>> =
@@ -333,12 +336,19 @@
}
private suspend fun createTile(spec: TileSpec): QSTile? {
- val tile = withContext(mainDispatcher) { tileFactory.createTile(spec.spec) }
+ val tile =
+ withContext(mainDispatcher) {
+ if (featureFlags.pipelineTilesEnabled) {
+ newQSTileFactory.get().createTile(spec.spec)
+ } else {
+ null
+ }
+ ?: tileFactory.createTile(spec.spec)
+ }
if (tile == null) {
logger.logTileNotFoundInFactory(spec)
return null
} else {
- tile.tileSpec = spec.spec
return if (!tile.isAvailable) {
logger.logTileDestroyed(
spec,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt
index 551b0f4..1a71b71 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt
@@ -1,7 +1,7 @@
package com.android.systemui.qs.pipeline.shared
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
import javax.inject.Inject
@@ -10,7 +10,7 @@
class QSPipelineFlagsRepository
@Inject
constructor(
- private val featureFlags: FeatureFlags,
+ private val featureFlags: FeatureFlagsClassic,
) {
/** @see Flags.QS_PIPELINE_NEW_HOST */
@@ -20,4 +20,8 @@
/** @see Flags.QS_PIPELINE_AUTO_ADD */
val pipelineAutoAddEnabled: Boolean
get() = pipelineHostEnabled && featureFlags.isEnabled(Flags.QS_PIPELINE_AUTO_ADD)
+
+ /** @see Flags.QS_PIPELINE_NEW_TILES */
+ val pipelineTilesEnabled: Boolean
+ get() = featureFlags.isEnabled(Flags.QS_PIPELINE_NEW_TILES)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt
index 11b5dd7..aed08f8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt
@@ -31,11 +31,7 @@
sealed class TileSpec private constructor(open val spec: String) {
/** Represents a spec that couldn't be parsed into a valid type of tile. */
- object Invalid : TileSpec("") {
- override fun toString(): String {
- return "TileSpec.INVALID"
- }
- }
+ data object Invalid : TileSpec("")
/** Container for the spec of a tile provided by SystemUI. */
data class PlatformTileSpec
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 9c7a734..632aa63 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -70,6 +70,7 @@
if (tile != null) {
tile.initialize();
tile.postStale(); // Tile was just created, must be stale.
+ tile.setTileSpec(tileSpec);
}
return tile;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandler.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandler.kt
index e9f907c..dc9e115 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserActionHandler.kt
@@ -1,11 +1,11 @@
package com.android.systemui.qs.tiles.base.actions
import android.content.Intent
+import android.view.View
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import javax.inject.Inject
/**
@@ -17,9 +17,9 @@
@Inject
constructor(private val activityStarter: ActivityStarter) {
- fun handle(userAction: QSTileUserAction, intent: Intent) {
+ fun handle(view: View?, intent: Intent) {
val animationController: ActivityLaunchAnimator.Controller? =
- userAction.view?.let {
+ view?.let {
ActivityLaunchAnimator.Controller.fromView(
it,
InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/BaseQSTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/BaseQSTileViewModel.kt
index c2a75fa..bb4de80 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/BaseQSTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/BaseQSTileViewModel.kt
@@ -92,7 +92,7 @@
.stateIn(
tileScope,
SharingStarted.WhileSubscribed(),
- false,
+ true,
)
private var currentLifeState: QSTileLifecycle = QSTileLifecycle.DEAD
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
new file mode 100644
index 0000000..3fedbfc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
@@ -0,0 +1,28 @@
+package com.android.systemui.qs.tiles.di
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.qs.QSFactory
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.tiles.viewmodel.QSTileLifecycle
+import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileViewModelAdapter
+import javax.inject.Inject
+import javax.inject.Provider
+
+// TODO(b/http://b/299909989): Rename the factory after rollout
+@SysUISingleton
+class NewQSTileFactory
+@Inject
+constructor(
+ private val adapterFactory: QSTileViewModelAdapter.Factory,
+ private val tileMap:
+ Map<String, @JvmSuppressWildcards Provider<@JvmSuppressWildcards QSTileViewModel>>,
+) : QSFactory {
+
+ override fun createTile(tileSpec: String): QSTile? =
+ tileMap[tileSpec]?.let {
+ val tile = it.get()
+ tile.onLifecycle(QSTileLifecycle.ALIVE)
+ adapterFactory.create(tile)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
index a5eaac1..019d3c0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
@@ -1,14 +1,13 @@
package com.android.systemui.qs.tiles.viewmodel
-import android.graphics.drawable.Icon
+import androidx.annotation.StringRes
+import com.android.internal.logging.InstanceId
+import com.android.systemui.common.shared.model.Icon
import com.android.systemui.qs.pipeline.shared.TileSpec
data class QSTileConfig(
val tileSpec: TileSpec,
val tileIcon: Icon,
- val tileLabel: CharSequence,
-// TODO(b/299908705): Fill necessary params
-/*
-val instanceId: InstanceId,
- */
+ @StringRes val tileLabelRes: Int,
+ val instanceId: InstanceId,
)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
index 53f9edf..dc5c690 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
@@ -1,18 +1,96 @@
package com.android.systemui.qs.tiles.viewmodel
-import android.graphics.drawable.Icon
+import android.service.quicksettings.Tile
+import com.android.systemui.common.shared.model.Icon
+/**
+ * Represents current a state of the tile to be displayed in on the view. Consider using
+ * [QSTileState.build] for better state creation experience and preset default values for certain
+ * fields.
+ *
+ * // TODO(b/http://b/299909989): Clean up legacy mappings after the transition
+ */
data class QSTileState(
- val icon: Icon,
+ val icon: () -> Icon,
val label: CharSequence,
-// TODO(b/299908705): Fill necessary params
-/*
- val subtitle: CharSequence = "",
- val activeState: ActivationState = Active,
- val enabledState: Enabled = Enabled,
- val loopIconAnimation: Boolean = false,
- val secondaryIcon: Icon? = null,
- val slashState: SlashState? = null,
- val supportedActions: Collection<UserAction> = listOf(Click), clicks should be a default action
-*/
-)
+ val activationState: ActivationState,
+ val secondaryLabel: CharSequence?,
+ val supportedActions: Set<UserAction>,
+ val contentDescription: CharSequence?,
+ val stateDescription: CharSequence?,
+ val sideViewIcon: SideViewIcon,
+ val enabledState: EnabledState,
+ val expandedAccessibilityClassName: String?,
+) {
+
+ companion object {
+
+ fun build(icon: () -> Icon, label: CharSequence, build: Builder.() -> Unit): QSTileState =
+ Builder(icon, label).apply(build).build()
+
+ fun build(icon: Icon, label: CharSequence, build: Builder.() -> Unit): QSTileState =
+ build({ icon }, label, build)
+ }
+
+ enum class ActivationState(val legacyState: Int) {
+ // An unavailable state indicates that for some reason this tile is not currently available
+ // to the user, and will have no click action. The tile's icon will be tinted differently to
+ // reflect this state.
+ UNAVAILABLE(Tile.STATE_UNAVAILABLE),
+ // This represents a tile that is currently active. (e.g. wifi is connected, bluetooth is
+ // on, cast is casting). This is the default state.
+ ACTIVE(Tile.STATE_ACTIVE),
+ // This represents a tile that is currently in a disabled state but is still interactable. A
+ // disabled state indicates that the tile is not currently active (e.g. wifi disconnected or
+ // bluetooth disabled), but is still interactable by the user to modify this state.
+ INACTIVE(Tile.STATE_INACTIVE),
+ }
+
+ /**
+ * Enabled tile behaves as usual where is disabled one is frozen and inactive in its current
+ * [ActivationState].
+ */
+ enum class EnabledState {
+ ENABLED,
+ DISABLED,
+ }
+
+ enum class UserAction {
+ CLICK,
+ LONG_CLICK,
+ }
+
+ sealed interface SideViewIcon {
+ data class Custom(val icon: Icon) : SideViewIcon
+ data object Chevron : SideViewIcon
+ data object None : SideViewIcon
+ }
+
+ class Builder(
+ var icon: () -> Icon,
+ var label: CharSequence,
+ ) {
+ var activationState: ActivationState = ActivationState.INACTIVE
+ var secondaryLabel: CharSequence? = null
+ var supportedActions: Set<UserAction> = setOf(UserAction.CLICK)
+ var contentDescription: CharSequence? = null
+ var stateDescription: CharSequence? = null
+ var sideViewIcon: SideViewIcon = SideViewIcon.None
+ var enabledState: EnabledState = EnabledState.ENABLED
+ var expandedAccessibilityClassName: String? = null
+
+ fun build(): QSTileState =
+ QSTileState(
+ icon,
+ label,
+ activationState,
+ secondaryLabel,
+ supportedActions,
+ contentDescription,
+ stateDescription,
+ sideViewIcon,
+ enabledState,
+ expandedAccessibilityClassName,
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileUserAction.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileUserAction.kt
index f1f8f01..0b232c2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileUserAction.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileUserAction.kt
@@ -1,13 +1,11 @@
package com.android.systemui.qs.tiles.viewmodel
-import android.content.Context
import android.view.View
sealed interface QSTileUserAction {
- val context: Context
val view: View?
- class Click(override val context: Context, override val view: View?) : QSTileUserAction
- class LongClick(override val context: Context, override val view: View?) : QSTileUserAction
+ class Click(override val view: View?) : QSTileUserAction
+ class LongClick(override val view: View?) : QSTileUserAction
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
new file mode 100644
index 0000000..d4bdb77
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
@@ -0,0 +1,232 @@
+package com.android.systemui.qs.tiles.viewmodel
+
+import android.content.Context
+import android.util.Log
+import android.view.View
+import androidx.annotation.GuardedBy
+import com.android.internal.logging.InstanceId
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon
+import com.android.systemui.qs.tileimpl.QSTileImpl.ResourceIcon
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import java.util.function.Supplier
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.cancelChildren
+import kotlinx.coroutines.flow.collectIndexed
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+
+// TODO(b/http://b/299909989): Use QSTileViewModel directly after the rollout
+class QSTileViewModelAdapter
+@AssistedInject
+constructor(
+ private val qsHost: QSHost,
+ @Assisted private val qsTileViewModel: QSTileViewModel,
+) : QSTile {
+
+ private val context
+ get() = qsHost.context
+
+ @GuardedBy("callbacks")
+ private val callbacks: MutableCollection<QSTile.Callback> = mutableSetOf()
+ @GuardedBy("listeningClients")
+ private val listeningClients: MutableCollection<Any> = mutableSetOf()
+
+ // Cancels the jobs when the adapter is no longer alive
+ private val adapterScope = CoroutineScope(SupervisorJob())
+ // Cancels the jobs when clients stop listening
+ private val listeningScope = CoroutineScope(SupervisorJob())
+
+ init {
+ adapterScope.launch {
+ qsTileViewModel.isAvailable.collectIndexed { index, isAvailable ->
+ if (!isAvailable) {
+ qsHost.removeTile(tileSpec)
+ }
+ // qsTileViewModel.isAvailable flow often starts with isAvailable == true. That's
+ // why we only allow isAvailable == true once and throw an exception afterwards.
+ if (index > 0 && isAvailable) {
+ // See com.android.systemui.qs.pipeline.domain.model.AutoAddable for additional
+ // guidance on how to auto add your tile
+ throw UnsupportedOperationException("Turning on tile is not supported now")
+ }
+ }
+ }
+
+ // QSTileHost doesn't call this when userId is initialized
+ userSwitch(qsHost.userId)
+
+ if (DEBUG) {
+ Log.d(TAG, "Using new tiles for: $tileSpec")
+ }
+ }
+
+ override fun isAvailable(): Boolean = qsTileViewModel.isAvailable.value
+
+ override fun setTileSpec(tileSpec: String?) {
+ throw UnsupportedOperationException("Tile spec is immutable in new tiles")
+ }
+
+ override fun refreshState() {
+ qsTileViewModel.forceUpdate()
+ }
+
+ override fun addCallback(callback: QSTile.Callback?) {
+ callback ?: return
+ synchronized(callbacks) { callbacks.add(callback) }
+ }
+
+ override fun removeCallback(callback: QSTile.Callback?) {
+ callback ?: return
+ synchronized(callbacks) { callbacks.remove(callback) }
+ }
+
+ override fun removeCallbacks() {
+ synchronized(callbacks) { callbacks.clear() }
+ }
+
+ override fun click(view: View?) {
+ if (isActionSupported(QSTileState.UserAction.CLICK)) {
+ qsTileViewModel.onActionPerformed(QSTileUserAction.Click(view))
+ }
+ }
+
+ override fun secondaryClick(view: View?) {
+ if (isActionSupported(QSTileState.UserAction.CLICK)) {
+ qsTileViewModel.onActionPerformed(QSTileUserAction.Click(view))
+ }
+ }
+
+ override fun longClick(view: View?) {
+ if (isActionSupported(QSTileState.UserAction.LONG_CLICK)) {
+ qsTileViewModel.onActionPerformed(QSTileUserAction.LongClick(view))
+ }
+ }
+
+ private fun isActionSupported(action: QSTileState.UserAction): Boolean =
+ qsTileViewModel.currentState?.supportedActions?.contains(action) == true
+
+ override fun userSwitch(currentUser: Int) {
+ qsTileViewModel.onUserIdChanged(currentUser)
+ }
+
+ @Deprecated(
+ "Not needed as {@link com.android.internal.logging.UiEvent} will use #getMetricsSpec",
+ replaceWith = ReplaceWith("getMetricsSpec"),
+ )
+ override fun getMetricsCategory(): Int = 0
+
+ override fun setListening(client: Any?, listening: Boolean) {
+ client ?: return
+ synchronized(listeningClients) {
+ if (listening) {
+ listeningClients.add(client)
+ if (listeningClients.size == 1) {
+ qsTileViewModel.state
+ .map { mapState(context, it, qsTileViewModel.config) }
+ .onEach { legacyState ->
+ synchronized(callbacks) {
+ callbacks.forEach { it.onStateChanged(legacyState) }
+ }
+ }
+ .launchIn(listeningScope)
+ }
+ } else {
+ listeningClients.remove(client)
+ if (listeningClients.isEmpty()) {
+ listeningScope.coroutineContext.cancelChildren()
+ }
+ }
+ }
+ }
+
+ override fun isListening(): Boolean =
+ synchronized(listeningClients) { listeningClients.isNotEmpty() }
+
+ override fun setDetailListening(show: Boolean) {
+ // do nothing like QSTileImpl
+ }
+
+ override fun destroy() {
+ adapterScope.cancel()
+ listeningScope.cancel()
+ qsTileViewModel.onLifecycle(QSTileLifecycle.DEAD)
+ }
+
+ override fun getState(): QSTile.State? =
+ qsTileViewModel.currentState?.let { mapState(context, it, qsTileViewModel.config) }
+
+ override fun getInstanceId(): InstanceId = qsTileViewModel.config.instanceId
+ override fun getTileLabel(): CharSequence =
+ context.getString(qsTileViewModel.config.tileLabelRes)
+ override fun getTileSpec(): String = qsTileViewModel.config.tileSpec.spec
+
+ private companion object {
+
+ const val DEBUG = false
+ const val TAG = "QSTileVMAdapter"
+
+ fun mapState(
+ context: Context,
+ viewModelState: QSTileState,
+ config: QSTileConfig
+ ): QSTile.State =
+ // we have to use QSTile.BooleanState to support different side icons
+ // which are bound to instanceof QSTile.BooleanState in QSTileView.
+ QSTile.BooleanState().apply {
+ spec = config.tileSpec.spec
+ label = viewModelState.label
+ // This value is synthetic and doesn't have any meaning
+ value = false
+
+ secondaryLabel = viewModelState.secondaryLabel
+ handlesLongClick =
+ viewModelState.supportedActions.contains(QSTileState.UserAction.LONG_CLICK)
+
+ iconSupplier = Supplier {
+ when (val stateIcon = viewModelState.icon()) {
+ is Icon.Loaded -> DrawableIcon(stateIcon.drawable)
+ is Icon.Resource -> ResourceIcon.get(stateIcon.res)
+ }
+ }
+ state = viewModelState.activationState.legacyState
+
+ contentDescription = viewModelState.contentDescription
+ stateDescription = viewModelState.stateDescription
+
+ disabledByPolicy = viewModelState.enabledState == QSTileState.EnabledState.DISABLED
+ expandedAccessibilityClassName = viewModelState.expandedAccessibilityClassName
+
+ when (viewModelState.sideViewIcon) {
+ is QSTileState.SideViewIcon.Custom -> {
+ sideViewCustomDrawable =
+ when (viewModelState.sideViewIcon.icon) {
+ is Icon.Loaded -> viewModelState.sideViewIcon.icon.drawable
+ is Icon.Resource ->
+ context.getDrawable(viewModelState.sideViewIcon.icon.res)
+ }
+ }
+ is QSTileState.SideViewIcon.Chevron -> {
+ forceExpandIcon = true
+ }
+ is QSTileState.SideViewIcon.None -> {
+ forceExpandIcon = false
+ }
+ }
+ }
+ }
+
+ @AssistedFactory
+ interface Factory {
+
+ fun create(qsTileViewModel: QSTileViewModel): QSTileViewModelAdapter
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
index b715237..6fa592c 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
@@ -30,12 +30,13 @@
import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.RestrictedLockUtils;
import com.android.systemui.Gefingerpoken;
-import com.android.systemui.res.R;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlagsClassic;
import com.android.systemui.haptics.slider.SeekableSliderEventProducer;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.util.ViewController;
@@ -283,6 +284,7 @@
private final VibratorHelper mVibratorHelper;
private final SystemClock mSystemClock;
private final CoroutineDispatcher mMainDispatcher;
+ private final ActivityStarter mActivityStarter;
@Inject
public Factory(
@@ -291,14 +293,15 @@
VibratorHelper vibratorHelper,
SystemClock clock,
FeatureFlagsClassic featureFlags,
- @Main CoroutineDispatcher mainDispatcher
- ) {
+ @Main CoroutineDispatcher mainDispatcher,
+ ActivityStarter activityStarter) {
mFalsingManager = falsingManager;
mUiEventLogger = uiEventLogger;
mFeatureFlags = featureFlags;
mVibratorHelper = vibratorHelper;
mSystemClock = clock;
mMainDispatcher = mainDispatcher;
+ mActivityStarter = activityStarter;
}
/**
@@ -314,6 +317,8 @@
int layout = getLayout();
BrightnessSliderView root = (BrightnessSliderView) LayoutInflater.from(context)
.inflate(layout, viewRoot, false);
+ root.setActivityStarter(mActivityStarter);
+
BrightnessSliderHapticPlugin plugin;
if (mFeatureFlags.isEnabled(HAPTIC_BRIGHTNESS_SLIDER)) {
plugin = new BrightnessSliderHapticPluginImpl(
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
index c885492..5ecf07f 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
@@ -33,6 +33,7 @@
import com.android.settingslib.RestrictedLockUtils;
import com.android.systemui.Gefingerpoken;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.res.R;
/**
@@ -41,6 +42,7 @@
*/
public class BrightnessSliderView extends FrameLayout {
+ private ActivityStarter mActivityStarter;
@NonNull
private ToggleSeekBar mSlider;
private DispatchTouchEventListener mListener;
@@ -57,6 +59,10 @@
super(context, attrs);
}
+ public void setActivityStarter(@NonNull ActivityStarter activityStarter) {
+ mActivityStarter = activityStarter;
+ }
+
// Inflated from quick_settings_brightness_dialog
@Override
protected void onFinishInflate() {
@@ -65,6 +71,7 @@
mSlider = requireViewById(R.id.slider);
mSlider.setAccessibilityLabel(getContentDescription().toString());
+ mSlider.setActivityStarter(mActivityStarter);
// Finds the progress drawable. Assumes brightness_progress_drawable.xml
try {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java
index a5a0ae7..6ec10da 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java
@@ -23,8 +23,9 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.SeekBar;
+import androidx.annotation.NonNull;
+
import com.android.settingslib.RestrictedLockUtils;
-import com.android.systemui.Dependency;
import com.android.systemui.plugins.ActivityStarter;
public class ToggleSeekBar extends SeekBar {
@@ -32,6 +33,8 @@
private RestrictedLockUtils.EnforcedAdmin mEnforcedAdmin = null;
+ private ActivityStarter mActivityStarter;
+
public ToggleSeekBar(Context context) {
super(context);
}
@@ -49,7 +52,7 @@
if (mEnforcedAdmin != null) {
Intent intent = RestrictedLockUtils.getShowAdminSupportDetailsIntent(
mContext, mEnforcedAdmin);
- Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(intent, 0);
+ mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
return true;
}
if (!isEnabled()) {
@@ -74,4 +77,8 @@
public void setEnforcedAdmin(RestrictedLockUtils.EnforcedAdmin admin) {
mEnforcedAdmin = admin;
}
+
+ public void setActivityStarter(@NonNull ActivityStarter activityStarter) {
+ mActivityStarter = activityStarter;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index 251cc16..ac8333a 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -129,6 +129,9 @@
combine(shadeExpansion, qsExpansion) { shadeExp, qsExp -> maxOf(shadeExp, qsExp) }
.stateIn(scope, SharingStarted.Eagerly, 0f)
+ /** Whether either the shade or QS is fully expanded. */
+ val isAnyFullyExpanded: Flow<Boolean> = anyExpansion.map { it >= 1f }.distinctUntilChanged()
+
/** Whether either the shade or QS is expanding from a fully collapsed state. */
val isAnyExpanding: Flow<Boolean> =
anyExpansion
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index ea5ca27..fb67139 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -49,8 +49,6 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.recents.OverviewProxyService;
@@ -103,7 +101,6 @@
private final BroadcastDispatcher mBroadcastDispatcher;
private final NotificationClickNotifier mClickNotifier;
private final Lazy<OverviewProxyService> mOverviewProxyServiceLazy;
- private final FeatureFlags mFeatureFlags;
private boolean mShowLockscreenNotifications;
private boolean mAllowLockscreenRemoteInput;
private LockPatternUtils mLockPatternUtils;
@@ -181,22 +178,7 @@
protected final UserTracker.Callback mUserChangedCallback =
new UserTracker.Callback() {
@Override
- public void onUserChanged(int newUser, @NonNull Context userContext) {
- if (!mFeatureFlags.isEnabled(
- Flags.LOAD_NOTIFICATIONS_BEFORE_THE_USER_SWITCH_IS_COMPLETE)) {
- handleUserChange(newUser);
- }
- }
-
- @Override
public void onUserChanging(int newUser, @NonNull Context userContext) {
- if (mFeatureFlags.isEnabled(
- Flags.LOAD_NOTIFICATIONS_BEFORE_THE_USER_SWITCH_IS_COMPLETE)) {
- handleUserChange(newUser);
- }
- }
-
- private void handleUserChange(int newUser) {
mCurrentUserId = newUser;
updateCurrentProfilesCache();
@@ -239,8 +221,7 @@
KeyguardStateController keyguardStateController,
SecureSettings secureSettings,
DumpManager dumpManager,
- LockPatternUtils lockPatternUtils,
- FeatureFlags featureFlags) {
+ LockPatternUtils lockPatternUtils) {
mContext = context;
mMainHandler = mainHandler;
mDevicePolicyManager = devicePolicyManager;
@@ -258,7 +239,6 @@
mDeviceProvisionedController = deviceProvisionedController;
mSecureSettings = secureSettings;
mKeyguardStateController = keyguardStateController;
- mFeatureFlags = featureFlags;
dumpManager.registerDumpable(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt
index efa092b..250fe53 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt
@@ -22,11 +22,12 @@
import androidx.annotation.VisibleForTesting
import com.android.settingslib.AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH
import com.android.settingslib.AccessibilityContentDescriptions.WIFI_NO_CONNECTION
-import com.android.systemui.res.R
+import com.android.settingslib.AccessibilityContentDescriptions.WIFI_OTHER_DEVICE_CONNECTION
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.log.table.Diffable
import com.android.systemui.log.table.TableRowLogger
+import com.android.systemui.res.R
import com.android.systemui.statusbar.connectivity.WifiIcons
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
@@ -90,50 +91,56 @@
)}"
)
)
- is WifiNetworkModel.Active -> {
- val levelDesc = context.getString(WIFI_CONNECTION_STRENGTH[model.level])
- val contentDescription =
- ContentDescription.Loaded(
- if (model.isValidated) {
- (levelDesc)
- } else {
- "$levelDesc,${context.getString(NO_INTERNET)}"
- }
- )
- Visible(model.toIcon(showHotspotInfo), contentDescription)
- }
+ is WifiNetworkModel.Active -> model.toIcon(showHotspotInfo, context)
}
- @DrawableRes
- private fun WifiNetworkModel.Active.toIcon(showHotspotInfo: Boolean): Int {
- return if (!showHotspotInfo) {
- this.toBasicIcon()
+ private fun WifiNetworkModel.Active.toIcon(
+ showHotspotInfo: Boolean,
+ context: Context,
+ ): Visible {
+ return if (
+ !showHotspotInfo ||
+ this.hotspotDeviceType == WifiNetworkModel.HotspotDeviceType.NONE
+ ) {
+ this.toBasicIcon(context)
} else {
- when (this.hotspotDeviceType) {
- WifiNetworkModel.HotspotDeviceType.NONE -> this.toBasicIcon()
- WifiNetworkModel.HotspotDeviceType.TABLET ->
- com.android.settingslib.R.drawable.ic_hotspot_tablet
- WifiNetworkModel.HotspotDeviceType.LAPTOP ->
- com.android.settingslib.R.drawable.ic_hotspot_laptop
- WifiNetworkModel.HotspotDeviceType.WATCH ->
- com.android.settingslib.R.drawable.ic_hotspot_watch
- WifiNetworkModel.HotspotDeviceType.AUTO ->
- com.android.settingslib.R.drawable.ic_hotspot_auto
- // Use phone as the default drawable
- WifiNetworkModel.HotspotDeviceType.PHONE,
- WifiNetworkModel.HotspotDeviceType.UNKNOWN,
- WifiNetworkModel.HotspotDeviceType.INVALID ->
- com.android.settingslib.R.drawable.ic_hotspot_phone
- }
+ val icon =
+ when (this.hotspotDeviceType) {
+ WifiNetworkModel.HotspotDeviceType.TABLET ->
+ com.android.settingslib.R.drawable.ic_hotspot_tablet
+ WifiNetworkModel.HotspotDeviceType.LAPTOP ->
+ com.android.settingslib.R.drawable.ic_hotspot_laptop
+ WifiNetworkModel.HotspotDeviceType.WATCH ->
+ com.android.settingslib.R.drawable.ic_hotspot_watch
+ WifiNetworkModel.HotspotDeviceType.AUTO ->
+ com.android.settingslib.R.drawable.ic_hotspot_auto
+ // Use phone as the default drawable
+ WifiNetworkModel.HotspotDeviceType.PHONE,
+ WifiNetworkModel.HotspotDeviceType.UNKNOWN,
+ WifiNetworkModel.HotspotDeviceType.INVALID ->
+ com.android.settingslib.R.drawable.ic_hotspot_phone
+ WifiNetworkModel.HotspotDeviceType.NONE ->
+ throw IllegalStateException("NONE checked earlier")
+ }
+ Visible(
+ icon,
+ ContentDescription.Loaded(context.getString(WIFI_OTHER_DEVICE_CONNECTION)),
+ )
}
}
- @DrawableRes
- private fun WifiNetworkModel.Active.toBasicIcon(): Int {
+ private fun WifiNetworkModel.Active.toBasicIcon(context: Context): Visible {
+ val levelDesc = context.getString(WIFI_CONNECTION_STRENGTH[this.level])
return if (this.isValidated) {
- WifiIcons.WIFI_FULL_ICONS[this.level]
+ Visible(
+ WifiIcons.WIFI_FULL_ICONS[this.level],
+ ContentDescription.Loaded(levelDesc),
+ )
} else {
- WifiIcons.WIFI_NO_INTERNET_ICONS[this.level]
+ Visible(
+ WifiIcons.WIFI_NO_INTERNET_ICONS[this.level],
+ ContentDescription.Loaded("$levelDesc,${context.getString(NO_INTERNET)}"),
+ )
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
index ae8128d..27f8121 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
@@ -67,7 +67,7 @@
private final ToastLogger mToastLogger;
@Nullable private ToastPresenter mPresenter;
@Nullable private ITransientNotificationCallback mCallback;
- private ToastOutAnimatorListener mToastOutAnimatorListener;
+ @VisibleForTesting ToastOutAnimatorListener mToastOutAnimatorListener;
@VisibleForTesting SystemUIToast mToast;
private int mOrientation = ORIENTATION_PORTRAIT;
@@ -172,7 +172,7 @@
if (mToast.getOutAnimation() != null) {
Animator animator = mToast.getOutAnimation();
mToastOutAnimatorListener = new ToastOutAnimatorListener(mPresenter, mCallback,
- runnable);
+ runnable, animator);
animator.addListener(mToastOutAnimatorListener);
animator.start();
} else {
@@ -211,14 +211,17 @@
final ToastPresenter mPrevPresenter;
final ITransientNotificationCallback mPrevCallback;
@Nullable Runnable mShowNextToastRunnable;
+ @NonNull private final Animator mAnimator;
ToastOutAnimatorListener(
@NonNull ToastPresenter presenter,
@NonNull ITransientNotificationCallback callback,
- @Nullable Runnable runnable) {
+ @Nullable Runnable runnable,
+ @NonNull Animator animator) {
mPrevPresenter = presenter;
mPrevCallback = callback;
mShowNextToastRunnable = runnable;
+ mAnimator = animator;
}
void setShowNextToastRunnable(Runnable runnable) {
@@ -231,6 +234,8 @@
if (mShowNextToastRunnable != null) {
mShowNextToastRunnable.run();
}
+ mAnimator.removeListener(this);
+ mShowNextToastRunnable = null;
mToastOutAnimatorListener = null;
}
}
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 5414259..677108c 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
@@ -18,10 +18,13 @@
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.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.activity.EmptyTestActivity
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -30,11 +33,13 @@
import com.android.wm.shell.taskview.TaskView
import com.google.common.truth.Truth.assertThat
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.any
+import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -43,31 +48,31 @@
@TestableLooper.RunWithLooper
class DetailDialogTest : SysuiTestCase() {
- @Mock
- private lateinit var taskView: TaskView
- @Mock
- private lateinit var broadcastSender: BroadcastSender
- @Mock
- private lateinit var controlViewHolder: ControlViewHolder
- @Mock
- private lateinit var pendingIntent: PendingIntent
- @Mock
- private lateinit var keyguardStateController: KeyguardStateController
- @Mock
- private lateinit var activityStarter: ActivityStarter
+ @Rule
+ @JvmField
+ val activityRule: ActivityScenarioRule<EmptyTestActivity> =
+ ActivityScenarioRule(EmptyTestActivity::class.java)
+
+ @Mock private lateinit var taskView: TaskView
+ @Mock private lateinit var broadcastSender: BroadcastSender
+ @Mock private lateinit var controlViewHolder: ControlViewHolder
+ @Mock private lateinit var pendingIntent: PendingIntent
+ @Mock private lateinit var keyguardStateController: KeyguardStateController
+ @Mock private lateinit var activityStarter: ActivityStarter
+
+ private lateinit var underTest: DetailDialog
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+
+ underTest = createDialog(pendingIntent)
}
@Test
fun testPendingIntentIsUnModified() {
- // GIVEN the dialog is created with a PendingIntent
- val dialog = createDialog(pendingIntent)
-
// WHEN the TaskView is initialized
- dialog.stateCallback.onInitialized()
+ underTest.stateCallback.onInitialized()
// THEN the PendingIntent used to call startActivity is unmodified by systemui
verify(taskView).startActivity(eq(pendingIntent), any(), any(), any())
@@ -75,11 +80,8 @@
@Test
fun testActivityOptionsAllowBal() {
- // GIVEN the dialog is created with a PendingIntent
- val dialog = createDialog(pendingIntent)
-
// WHEN the TaskView is initialized
- dialog.stateCallback.onInitialized()
+ underTest.stateCallback.onInitialized()
val optionsCaptor = argumentCaptor<ActivityOptions>()
@@ -90,17 +92,41 @@
.isEqualTo(ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
assertThat(optionsCaptor.value.isPendingIntentBackgroundActivityLaunchAllowedByPermission)
.isTrue()
+ assertThat(optionsCaptor.value.taskAlwaysOnTop).isTrue()
}
- private fun createDialog(pendingIntent: PendingIntent): DetailDialog {
+ @Test
+ fun testDismissRemovesTheTask() {
+ activityRule.scenario.onActivity {
+ underTest = createDialog(pendingIntent, it)
+ underTest.show()
+
+ underTest.dismiss()
+
+ verify(taskView).removeTask()
+ verify(taskView, never()).release()
+ }
+ }
+
+ @Test
+ fun testTaskRemovalReleasesTaskView() {
+ underTest.stateCallback.onTaskRemovalStarted(0)
+
+ verify(taskView).release()
+ }
+
+ private fun createDialog(
+ pendingIntent: PendingIntent,
+ context: Context = mContext,
+ ): DetailDialog {
return DetailDialog(
- mContext,
+ context,
broadcastSender,
taskView,
pendingIntent,
controlViewHolder,
keyguardStateController,
- activityStarter
+ activityStarter,
)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
index 25faeef..de57b60 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
@@ -81,6 +81,7 @@
@Mock private lateinit var mediaDataManager: MediaDataManager
@Mock private lateinit var uniqueObjectHostView: UniqueObjectHostView
@Mock private lateinit var dreamOverlayStateController: DreamOverlayStateController
+ @Mock lateinit var logger: MediaViewLogger
@Captor
private lateinit var wakefullnessObserver: ArgumentCaptor<(WakefulnessLifecycle.Observer)>
@Captor
@@ -121,7 +122,8 @@
notifPanelEvents,
settings,
fakeHandler,
- ResourcesSplitShadeStateController()
+ ResourcesSplitShadeStateController(),
+ logger,
)
verify(wakefulnessLifecycle).addObserver(wakefullnessObserver.capture())
verify(statusBarStateController).addCallback(statusBarCallback.capture())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index b595e8d..79411f42 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -66,6 +66,7 @@
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.qs.tiles.di.NewQSTileFactory;
import com.android.systemui.settings.UserFileManager;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeController;
@@ -77,6 +78,8 @@
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.FakeSystemClock;
+import dagger.Lazy;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -102,8 +105,6 @@
private static final String SETTING = QSHost.TILES_SETTING;
@Mock
- private QSFactory mDefaultFactory;
- @Mock
private PluginManager mPluginManager;
@Mock
private TunerService mTunerService;
@@ -117,7 +118,6 @@
private CustomTile mCustomTile;
@Mock
private UserTracker mUserTracker;
- private SecureSettings mSecureSettings;
@Mock
private CustomTileStatePersister mCustomTileStatePersister;
@Mock
@@ -127,6 +127,10 @@
@Mock
private UserFileManager mUserFileManager;
+ private SecureSettings mSecureSettings;
+
+ private QSFactory mDefaultFactory;
+
private SparseArray<SharedPreferences> mSharedPreferencesByUser;
private FakeFeatureFlags mFeatureFlags;
@@ -144,6 +148,8 @@
mFeatureFlags.set(Flags.QS_PIPELINE_NEW_HOST, false);
mFeatureFlags.set(Flags.QS_PIPELINE_AUTO_ADD, false);
+ // TODO(b/299909337): Add test checking the new factory is used when the flag is on
+ mFeatureFlags.set(Flags.QS_PIPELINE_NEW_TILES, false);
mQSPipelineFlagsRepository = new QSPipelineFlagsRepository(mFeatureFlags);
mMainExecutor = new FakeExecutor(new FakeSystemClock());
@@ -164,7 +170,8 @@
mSecureSettings = new FakeSettings();
saveSetting("");
- mQSTileHost = new TestQSTileHost(mContext, mDefaultFactory, mMainExecutor,
+ setUpTileFactory();
+ mQSTileHost = new TestQSTileHost(mContext, () -> null, mDefaultFactory, mMainExecutor,
mPluginManager, mTunerService, () -> mAutoTiles, mShadeController,
mQSLogger, mUserTracker, mSecureSettings, mCustomTileStatePersister,
mTileLifecycleManagerFactory, mUserFileManager, mQSPipelineFlagsRepository);
@@ -178,7 +185,6 @@
mMainExecutor.runAllReady();
}
}, mUserTracker.getUserId());
- setUpTileFactory();
}
private void saveSetting(String value) {
@@ -191,32 +197,29 @@
}
private void setUpTileFactory() {
- // Only create this kind of tiles
- when(mDefaultFactory.createTile(anyString())).thenAnswer(
- invocation -> {
- String spec = invocation.getArgument(0);
- if ("spec1".equals(spec)) {
- return new TestTile1(mQSTileHost);
- } else if ("spec2".equals(spec)) {
- return new TestTile2(mQSTileHost);
- } else if ("spec3".equals(spec)) {
- return new TestTile3(mQSTileHost);
- } else if ("na".equals(spec)) {
- return new NotAvailableTile(mQSTileHost);
- } else if (CUSTOM_TILE_SPEC.equals(spec)) {
- QSTile tile = mCustomTile;
- QSTile.State s = mock(QSTile.State.class);
- s.spec = spec;
- when(mCustomTile.getState()).thenReturn(s);
- return tile;
- } else if ("internet".equals(spec)
- || "wifi".equals(spec)
- || "cell".equals(spec)) {
- return new TestTile1(mQSTileHost);
- } else {
- return null;
- }
- });
+ mDefaultFactory = new FakeQSFactory(spec -> {
+ if ("spec1".equals(spec)) {
+ return new TestTile1(mQSTileHost);
+ } else if ("spec2".equals(spec)) {
+ return new TestTile2(mQSTileHost);
+ } else if ("spec3".equals(spec)) {
+ return new TestTile3(mQSTileHost);
+ } else if ("na".equals(spec)) {
+ return new NotAvailableTile(mQSTileHost);
+ } else if (CUSTOM_TILE_SPEC.equals(spec)) {
+ QSTile tile = mCustomTile;
+ QSTile.State s = mock(QSTile.State.class);
+ s.spec = spec;
+ when(mCustomTile.getState()).thenReturn(s);
+ return tile;
+ } else if ("internet".equals(spec)
+ || "wifi".equals(spec)
+ || "cell".equals(spec)) {
+ return new TestTile1(mQSTileHost);
+ } else {
+ return null;
+ }
+ });
when(mCustomTile.isAvailable()).thenReturn(true);
}
@@ -703,7 +706,7 @@
}
private class TestQSTileHost extends QSTileHost {
- TestQSTileHost(Context context,
+ TestQSTileHost(Context context, Lazy<NewQSTileFactory> newQSTileFactoryProvider,
QSFactory defaultFactory, Executor mainExecutor,
PluginManager pluginManager, TunerService tunerService,
Provider<AutoTileManager> autoTiles,
@@ -712,7 +715,7 @@
CustomTileStatePersister customTileStatePersister,
TileLifecycleManager.Factory tileLifecycleManagerFactory,
UserFileManager userFileManager, QSPipelineFlagsRepository featureFlags) {
- super(context, defaultFactory, mainExecutor, pluginManager,
+ super(context, newQSTileFactoryProvider, defaultFactory, mainExecutor, pluginManager,
tunerService, autoTiles, shadeController, qsLogger,
userTracker, secureSettings, customTileStatePersister,
tileLifecycleManagerFactory, userFileManager, featureFlags);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index bde3038..d3cd26b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -124,6 +124,7 @@
if (FACTORY_TILES.contains(spec)) {
FakeQSTile tile = new FakeQSTile(mBgExecutor, mMainExecutor);
tile.setState(mState);
+ tile.setTileSpec(spec);
return tile;
} else {
return null;
@@ -284,7 +285,10 @@
Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.QS_TILES, null);
QSTile t = mock(QSTile.class);
- when(mQSHost.createTile("hotspot")).thenReturn(t);
+ when(mQSHost.createTile("hotspot")).thenAnswer(invocation -> {
+ t.setTileSpec("hotspot");
+ return t;
+ });
mContext.getOrCreateTestableResources().addOverride(R.string.quick_settings_tiles_stock,
"hotspot");
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
index dc1b9c4..a750524 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
@@ -46,6 +46,7 @@
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.android.systemui.qs.tiles.di.NewQSTileFactory
import com.android.systemui.qs.toProto
import com.android.systemui.settings.UserTracker
import com.android.systemui.user.data.repository.FakeUserRepository
@@ -91,6 +92,8 @@
@Mock private lateinit var logger: QSPipelineLogger
+ @Mock private lateinit var newQSTileFactory: NewQSTileFactory
+
private val testDispatcher = StandardTestDispatcher()
private val testScope = TestScope(testDispatcher)
@@ -105,6 +108,8 @@
featureFlags.set(Flags.QS_PIPELINE_NEW_HOST, true)
featureFlags.set(Flags.QS_PIPELINE_AUTO_ADD, true)
+ // TODO(b/299909337): Add test checking the new factory is used when the flag is on
+ featureFlags.set(Flags.QS_PIPELINE_NEW_TILES, true)
userRepository.setUserInfos(listOf(USER_INFO_0, USER_INFO_1))
@@ -117,6 +122,7 @@
userRepository = userRepository,
customTileStatePersister = customTileStatePersister,
tileFactory = tileFactory,
+ newQSTileFactory = { newQSTileFactory },
customTileAddedRepository = customTileAddedRepository,
tileLifecycleManagerFactory = tileLifecycleManagerFactory,
userTracker = userTracker,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/QSTileIntentUserActionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/QSTileIntentUserActionHandlerTest.kt
index 47b4244..077c813 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/QSTileIntentUserActionHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/QSTileIntentUserActionHandlerTest.kt
@@ -6,7 +6,6 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserActionHandler
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -34,7 +33,7 @@
fun testPassesIntentToStarter() {
val intent = Intent("test.ACTION")
- underTest.handle(QSTileUserAction.Click(context, null), intent)
+ underTest.handle(null, intent)
verify(activityStarted).postStartActivityDismissingKeyguard(eq(intent), eq(0), any())
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
index 643866e..eacb080 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
@@ -1,11 +1,14 @@
package com.android.systemui.qs.tiles.viewmodel
-import android.graphics.drawable.Icon
+import android.graphics.drawable.ShapeDrawable
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.MediumTest
+import com.android.internal.logging.InstanceId
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tiles.base.interactor.FakeQSTileDataInteractor
import com.android.systemui.qs.tiles.base.interactor.FakeQSTileUserActionInteractor
@@ -71,20 +74,21 @@
fakeQSTileUserActionInteractor,
fakeQSTileDataInteractor,
object : QSTileDataToStateMapper<Any> {
- override fun map(config: QSTileConfig, data: Any): QSTileState {
- return QSTileState(config.tileIcon, config.tileLabel)
- }
+ override fun map(config: QSTileConfig, data: Any): QSTileState =
+ QSTileState.build(Icon.Resource(0, ContentDescription.Resource(0)), "") {}
},
testCoroutineDispatcher,
tileScope = scope.backgroundScope,
) {}
private companion object {
+
val TEST_QS_TILE_CONFIG =
QSTileConfig(
TileSpec.create("default"),
- Icon.createWithContentUri(""),
- "",
+ Icon.Loaded(ShapeDrawable(), null),
+ 0,
+ InstanceId.fakeInstanceId(0),
)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 39accfb..0bc79a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -49,8 +49,6 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.settings.UserTracker;
@@ -112,7 +110,6 @@
private NotificationEntry mCurrentUserNotif;
private NotificationEntry mSecondaryUserNotif;
private NotificationEntry mWorkProfileNotif;
- private final FakeFeatureFlags mFakeFeatureFlags = new FakeFeatureFlags();
@Before
public void setUp() {
@@ -295,21 +292,12 @@
@Test
public void testUserSwitchedCallsOnUserSwitching() {
- mFakeFeatureFlags.set(Flags.LOAD_NOTIFICATIONS_BEFORE_THE_USER_SWITCH_IS_COMPLETE, true);
mLockscreenUserManager.getUserTrackerCallbackForTest().onUserChanging(mSecondaryUser.id,
mContext);
verify(mPresenter, times(1)).onUserSwitched(mSecondaryUser.id);
}
@Test
- public void testUserSwitchedCallsOnUserSwitched() {
- mFakeFeatureFlags.set(Flags.LOAD_NOTIFICATIONS_BEFORE_THE_USER_SWITCH_IS_COMPLETE, false);
- mLockscreenUserManager.getUserTrackerCallbackForTest().onUserChanged(mSecondaryUser.id,
- mContext);
- verify(mPresenter, times(1)).onUserSwitched(mSecondaryUser.id);
- }
-
- @Test
public void testIsLockscreenPublicMode() {
assertFalse(mLockscreenUserManager.isLockscreenPublicMode(mCurrentUser.id));
mLockscreenUserManager.setLockscreenPublicMode(true, mCurrentUser.id);
@@ -368,8 +356,7 @@
mKeyguardStateController,
mSettings,
mock(DumpManager.class),
- mock(LockPatternUtils.class),
- mFakeFeatureFlags);
+ mock(LockPatternUtils.class));
}
public BroadcastReceiver getBaseBroadcastReceiverForTest() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
index 15b9d61..c935dbb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
@@ -17,13 +17,14 @@
package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
+import com.android.settingslib.AccessibilityContentDescriptions.WIFI_OTHER_DEVICE_CONNECTION
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
import com.android.systemui.common.shared.model.Text
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.qs.tileimpl.QSTileImpl.ResourceIcon
+import com.android.systemui.res.R
import com.android.systemui.statusbar.connectivity.WifiIcons
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.ethernet.domain.EthernetInteractor
@@ -181,6 +182,8 @@
assertThat(latest?.icon)
.isEqualTo(ResourceIcon.get(WifiIcons.WIFI_NO_INTERNET_ICONS[4]))
+ assertThat(latest?.stateDescription.loadContentDescription(context))
+ .doesNotContain(context.getString(WIFI_OTHER_DEVICE_CONNECTION))
}
@Test
@@ -192,6 +195,8 @@
assertThat(latest?.icon)
.isEqualTo(ResourceIcon.get(com.android.settingslib.R.drawable.ic_hotspot_tablet))
+ assertThat(latest?.stateDescription.loadContentDescription(context))
+ .isEqualTo(context.getString(WIFI_OTHER_DEVICE_CONNECTION))
}
@Test
@@ -203,6 +208,8 @@
assertThat(latest?.icon)
.isEqualTo(ResourceIcon.get(com.android.settingslib.R.drawable.ic_hotspot_laptop))
+ assertThat(latest?.stateDescription.loadContentDescription(context))
+ .isEqualTo(context.getString(WIFI_OTHER_DEVICE_CONNECTION))
}
@Test
@@ -214,6 +221,8 @@
assertThat(latest?.icon)
.isEqualTo(ResourceIcon.get(com.android.settingslib.R.drawable.ic_hotspot_watch))
+ assertThat(latest?.stateDescription.loadContentDescription(context))
+ .isEqualTo(context.getString(WIFI_OTHER_DEVICE_CONNECTION))
}
@Test
@@ -225,6 +234,8 @@
assertThat(latest?.icon)
.isEqualTo(ResourceIcon.get(com.android.settingslib.R.drawable.ic_hotspot_auto))
+ assertThat(latest?.stateDescription.loadContentDescription(context))
+ .isEqualTo(context.getString(WIFI_OTHER_DEVICE_CONNECTION))
}
@Test
@@ -236,6 +247,8 @@
assertThat(latest?.icon)
.isEqualTo(ResourceIcon.get(com.android.settingslib.R.drawable.ic_hotspot_phone))
+ assertThat(latest?.stateDescription.loadContentDescription(context))
+ .isEqualTo(context.getString(WIFI_OTHER_DEVICE_CONNECTION))
}
@Test
@@ -247,6 +260,8 @@
assertThat(latest?.icon)
.isEqualTo(ResourceIcon.get(com.android.settingslib.R.drawable.ic_hotspot_phone))
+ assertThat(latest?.stateDescription.loadContentDescription(context))
+ .isEqualTo(context.getString(WIFI_OTHER_DEVICE_CONNECTION))
}
@Test
@@ -258,6 +273,8 @@
assertThat(latest?.icon)
.isEqualTo(ResourceIcon.get(com.android.settingslib.R.drawable.ic_hotspot_phone))
+ assertThat(latest?.stateDescription.loadContentDescription(context))
+ .isEqualTo(context.getString(WIFI_OTHER_DEVICE_CONNECTION))
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
index a520f6c..49a2648 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
@@ -18,8 +18,10 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.settingslib.AccessibilityContentDescriptions.WIFI_OTHER_DEVICE_CONNECTION
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.connectivity.WifiIcons
@@ -136,6 +138,10 @@
// is used instead
assertThat(latest).isInstanceOf(WifiIcon.Visible::class.java)
assertThat((latest as WifiIcon.Visible).res).isEqualTo(WifiIcons.WIFI_FULL_ICONS[1])
+ assertThat(
+ (latest as WifiIcon.Visible).contentDescription.loadContentDescription(context)
+ )
+ .doesNotContain(context.getString(WIFI_OTHER_DEVICE_CONNECTION))
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
index 85359ca..bfc5bdb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
@@ -342,6 +342,31 @@
}
@Test
+ public void testShowToast_afterShowToast_animationListenerCleanup() throws RemoteException {
+ mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
+ mCallback, Display.DEFAULT_DISPLAY);
+ final SystemUIToast toast = mToastUI.mToast;
+
+ View view = verifyWmAddViewAndAttachToParent();
+ mToastUI.showToast(UID_2, PACKAGE_NAME_2, TOKEN_2, TEXT, WINDOW_TOKEN_2, Toast.LENGTH_LONG,
+ null, Display.DEFAULT_DISPLAY);
+
+ if (toast.getOutAnimation() != null) {
+ assertThat(mToastUI.mToastOutAnimatorListener).isNotNull();
+ assertThat(toast.getOutAnimation().getListeners()
+ .contains(mToastUI.mToastOutAnimatorListener)).isTrue();
+ assertThat(toast.getOutAnimation().isRunning()).isTrue();
+ toast.getOutAnimation().cancel(); // end early if applicable
+ assertThat(toast.getOutAnimation().getListeners()).isNull();
+ }
+
+ verify(mWindowManager).removeViewImmediate(view);
+ verify(mNotificationManager).finishToken(PACKAGE_NAME_1, TOKEN_1);
+ verify(mCallback).onToastHidden();
+ assertThat(mToastUI.mToastOutAnimatorListener).isNull();
+ }
+
+ @Test
public void testShowToast_logs() {
mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
mCallback, Display.DEFAULT_DISPLAY);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSFactory.kt
index bf26e71..cbf4ae5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSFactory.kt
@@ -21,6 +21,6 @@
class FakeQSFactory(private val tileCreator: (String) -> QSTile?) : QSFactory {
override fun createTile(tileSpec: String): QSTile? {
- return tileCreator(tileSpec)
+ return tileCreator(tileSpec)?.also { it.tileSpec = tileSpec }
}
}
diff --git a/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING b/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING
index 253ef43..3583a78 100644
--- a/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING
+++ b/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING
@@ -39,7 +39,7 @@
"include-filter": "android.hardware.input.cts.tests"
},
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
],
"file_patterns": ["Virtual[^/]*\\.java"]
diff --git a/services/core/java/com/android/server/display/TEST_MAPPING b/services/core/java/com/android/server/display/TEST_MAPPING
index 6f243e1..049b2fd 100644
--- a/services/core/java/com/android/server/display/TEST_MAPPING
+++ b/services/core/java/com/android/server/display/TEST_MAPPING
@@ -4,7 +4,6 @@
"name": "DisplayServiceTests",
"options": [
{"include-filter": "com.android.server.display"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "org.junit.Ignore"}
]
diff --git a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java
index 41053e9..68848a2 100644
--- a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java
+++ b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java
@@ -16,36 +16,66 @@
package com.android.server.grammaticalinflection;
+import static android.app.Flags.systemTermsOfAddressEnabled;
import static android.content.res.Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED;
import android.annotation.Nullable;
+import android.app.GrammaticalInflectionManager;
import android.app.IGrammaticalInflectionManager;
import android.content.Context;
import android.content.pm.PackageManagerInternal;
import android.os.Binder;
-import android.os.IBinder;
+import android.os.Environment;
import android.os.Process;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
import android.os.SystemProperties;
+import android.util.AtomicFile;
import android.util.Log;
+import android.util.SparseIntArray;
+import android.util.Xml;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.internal.util.XmlUtils;
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.wm.ActivityTaskManagerInternal;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
/**
* The implementation of IGrammaticalInflectionManager.aidl.
*
* <p>This service is API entry point for storing app-specific grammatical inflection.
*/
public class GrammaticalInflectionService extends SystemService {
- private final String TAG = "GrammaticalInflection";
- private final GrammaticalInflectionBackupHelper mBackupHelper;
- private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
- private PackageManagerInternal mPackageManagerInternal;
+ private static final String TAG = "GrammaticalInflection";
+ private static final String ATTR_NAME = "grammatical_gender";
+ private static final String USER_SETTINGS_FILE_NAME = "user_settings.xml";
+ private static final String TAG_GRAMMATICAL_INFLECTION = "grammatical_inflection";
private static final String GRAMMATICAL_INFLECTION_ENABLED =
"i18n.grammatical_Inflection.enabled";
+ private final GrammaticalInflectionBackupHelper mBackupHelper;
+ private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
+ private final Object mLock = new Object();
+ private final SparseIntArray mGrammaticalGenderCache = new SparseIntArray();
+
+ private PackageManagerInternal mPackageManagerInternal;
+ private GrammaticalInflectionService.GrammaticalInflectionBinderService mBinderService;
+
/**
* Initializes the system service.
* <p>
@@ -62,22 +92,46 @@
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mBackupHelper = new GrammaticalInflectionBackupHelper(
this, context.getPackageManager());
+ mBinderService = new GrammaticalInflectionBinderService();
}
@Override
public void onStart() {
- publishBinderService(Context.GRAMMATICAL_INFLECTION_SERVICE, mService);
+ publishBinderService(Context.GRAMMATICAL_INFLECTION_SERVICE, mBinderService);
LocalServices.addService(GrammaticalInflectionManagerInternal.class,
new GrammaticalInflectionManagerInternalImpl());
}
- private final IBinder mService = new IGrammaticalInflectionManager.Stub() {
+ private final class GrammaticalInflectionBinderService extends
+ IGrammaticalInflectionManager.Stub {
@Override
public void setRequestedApplicationGrammaticalGender(
String appPackageName, int userId, int gender) {
GrammaticalInflectionService.this.setRequestedApplicationGrammaticalGender(
appPackageName, userId, gender);
}
+
+ @Override
+ public void setSystemWideGrammaticalGender(int userId, int grammaticalGender) {
+ checkCallerIsSystem();
+ checkSystemTermsOfAddressIsEnabled();
+ GrammaticalInflectionService.this.setSystemWideGrammaticalGender(grammaticalGender,
+ userId);
+ }
+
+ @Override
+ public int getSystemGrammaticalGender(int userId) {
+ checkSystemTermsOfAddressIsEnabled();
+ return GrammaticalInflectionService.this.getSystemGrammaticalGender(userId);
+ }
+
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out,
+ FileDescriptor err, String[] args, ShellCallback callback,
+ ResultReceiver resultReceiver) {
+ (new GrammaticalInflectionShellCommand(mBinderService))
+ .exec(this, in, out, err, args, callback, resultReceiver);
+ }
};
private final class GrammaticalInflectionManagerInternalImpl
@@ -94,12 +148,6 @@
public void stageAndApplyRestoredPayload(byte[] payload, int userId) {
mBackupHelper.stageAndApplyRestoredPayload(payload, userId);
}
-
- private void checkCallerIsSystem() {
- if (Binder.getCallingUid() != Process.SYSTEM_UID) {
- throw new SecurityException("Caller is not system.");
- }
- }
}
protected int getApplicationGrammaticalGender(String appPackageName, int userId) {
@@ -137,4 +185,105 @@
updater.setGrammaticalGender(gender).commit();
}
+
+ protected void setSystemWideGrammaticalGender(int grammaticalGender, int userId) {
+ if (!GrammaticalInflectionManager.VALID_GRAMMATICAL_GENDER_VALUES.contains(
+ grammaticalGender)) {
+ throw new IllegalArgumentException("Unknown grammatical gender");
+ }
+
+ synchronized (mLock) {
+ final File file = getGrammaticalGenderFile(userId);
+ final AtomicFile atomicFile = new AtomicFile(file);
+ FileOutputStream stream = null;
+ try {
+ stream = atomicFile.startWrite();
+ stream.write(toXmlByteArray(grammaticalGender, stream));
+ atomicFile.finishWrite(stream);
+ mGrammaticalGenderCache.put(userId, grammaticalGender);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to write file " + atomicFile, e);
+ if (stream != null) {
+ atomicFile.failWrite(stream);
+ }
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ // TODO(b/298591009): Add a new AppOp value for the apps that want to access the grammatical
+ // gender.
+ public int getSystemGrammaticalGender(int userId) {
+ synchronized (mLock) {
+ final File file = getGrammaticalGenderFile(userId);
+ if (!file.exists()) {
+ Log.d(TAG, "User " + userId + "doesn't have the grammatical gender file.");
+ return GRAMMATICAL_GENDER_NOT_SPECIFIED;
+ }
+
+ if (mGrammaticalGenderCache.indexOfKey(userId) < 0) {
+ try {
+ InputStream in = new FileInputStream(file);
+ final TypedXmlPullParser parser = Xml.resolvePullParser(in);
+ mGrammaticalGenderCache.put(userId, getGrammaticalGenderFromXml(parser));
+ } catch (IOException | XmlPullParserException e) {
+ Log.e(TAG, "Failed to parse XML configuration from " + file, e);
+ }
+ }
+ return mGrammaticalGenderCache.get(userId);
+ }
+ }
+
+ private File getGrammaticalGenderFile(int userId) {
+ final File dir = new File(Environment.getDataSystemCeDirectory(userId),
+ TAG_GRAMMATICAL_INFLECTION);
+ return new File(dir, USER_SETTINGS_FILE_NAME);
+ }
+
+ private byte[] toXmlByteArray(int grammaticalGender, FileOutputStream fileStream) {
+
+ try {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ TypedXmlSerializer out = Xml.resolveSerializer(fileStream);
+ out.setOutput(outputStream, StandardCharsets.UTF_8.name());
+ out.startDocument(/* encoding= */ null, /* standalone= */ true);
+ out.startTag(null, TAG_GRAMMATICAL_INFLECTION);
+ out.attributeInt(null, ATTR_NAME, grammaticalGender);
+ out.endTag(null, TAG_GRAMMATICAL_INFLECTION);
+ out.endDocument();
+
+ return outputStream.toByteArray();
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ private int getGrammaticalGenderFromXml(TypedXmlPullParser parser)
+ throws IOException, XmlPullParserException {
+
+ XmlUtils.nextElement(parser);
+ while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+ String tagName = parser.getName();
+ if (TAG_GRAMMATICAL_INFLECTION.equals(tagName)) {
+ return parser.getAttributeInt(null, ATTR_NAME);
+ } else {
+ XmlUtils.nextElement(parser);
+ }
+ }
+
+ return GRAMMATICAL_GENDER_NOT_SPECIFIED;
+ }
+
+ private void checkCallerIsSystem() {
+ int callingUid = Binder.getCallingUid();
+ if (callingUid != Process.SYSTEM_UID && callingUid != Process.SHELL_UID) {
+ throw new SecurityException("Caller is not system and shell.");
+ }
+ }
+
+ private void checkSystemTermsOfAddressIsEnabled() {
+ if (!systemTermsOfAddressEnabled()) {
+ throw new RuntimeException("The flag must be enabled to allow calling the API.");
+ }
+ }
}
diff --git a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionShellCommand.java b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionShellCommand.java
new file mode 100644
index 0000000..d223728
--- /dev/null
+++ b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionShellCommand.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.grammaticalinflection;
+
+import static android.content.res.Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED;
+
+import android.app.ActivityManager;
+import android.app.GrammaticalInflectionManager;
+import android.app.IGrammaticalInflectionManager;
+import android.content.res.Configuration;
+import android.os.RemoteException;
+import android.os.ShellCommand;
+import android.os.UserHandle;
+import android.util.SparseArray;
+
+import java.io.PrintWriter;
+
+/**
+ * Shell commands for {@link GrammaticalInflectionService}
+ */
+class GrammaticalInflectionShellCommand extends ShellCommand {
+
+ private static final SparseArray<String> GRAMMATICAL_GENDER_MAP = new SparseArray<>();
+ static {
+ GRAMMATICAL_GENDER_MAP.put(Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED,
+ "Not specified (0)");
+ GRAMMATICAL_GENDER_MAP.put(Configuration.GRAMMATICAL_GENDER_NEUTRAL, "Neuter (1)");
+ GRAMMATICAL_GENDER_MAP.put(Configuration.GRAMMATICAL_GENDER_FEMININE, "Feminine (2)");
+ GRAMMATICAL_GENDER_MAP.put(Configuration.GRAMMATICAL_GENDER_MASCULINE, "Masculine (3)");
+ }
+
+ private final IGrammaticalInflectionManager mBinderService;
+
+ GrammaticalInflectionShellCommand(IGrammaticalInflectionManager grammaticalInflectionManager) {
+ mBinderService = grammaticalInflectionManager;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+ switch (cmd) {
+ case "set-system-grammatical-gender":
+ return runSetSystemWideGrammaticalGender();
+ case "get-system-grammatical-gender":
+ return runGetSystemGrammaticalGender();
+ default: {
+ return handleDefaultCommands(cmd);
+ }
+ }
+ }
+
+ @Override
+ public void onHelp() {
+ PrintWriter pw = getOutPrintWriter();
+ pw.println("Grammatical inflection manager (grammatical_inflection) shell commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println(
+ " set-system-grammatical-gender [--user <USER_ID>] [--grammaticalGender "
+ + "<GRAMMATICAL_GENDER>]");
+ pw.println(" Set the system grammatical gender for system.");
+ pw.println(" --user <USER_ID>: apply for the given user, "
+ + "the current user is used when unspecified.");
+ pw.println(
+ " --grammaticalGender <GRAMMATICAL_GENDER>: The terms of address the user "
+ + "preferred in system, not specified (0) is used when unspecified.");
+ pw.println(
+ " eg. 0 = not_specified, 1 = neuter, 2 = feminine, 3 = masculine"
+ + ".");
+ pw.println(
+ " get-system-grammatical-gender [--user <USER_ID>]");
+ pw.println(" Get the system grammatical gender for system.");
+ pw.println(" --user <USER_ID>: apply for the given user, "
+ + "the current user is used when unspecified.");
+ }
+
+ private int runSetSystemWideGrammaticalGender() {
+ int userId = ActivityManager.getCurrentUser();
+ int grammaticalGender = GRAMMATICAL_GENDER_NOT_SPECIFIED;
+ do {
+ String option = getNextOption();
+ if (option == null) {
+ break;
+ }
+ switch (option) {
+ case "--user": {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ break;
+ }
+ case "-g":
+ case "--grammaticalGender": {
+ grammaticalGender = parseGrammaticalGender();
+ break;
+ }
+ default: {
+ throw new IllegalArgumentException("Unknown option: " + option);
+ }
+ }
+ } while (true);
+
+ try {
+ mBinderService.setSystemWideGrammaticalGender(userId, grammaticalGender);
+ } catch (RemoteException e) {
+ getOutPrintWriter().println("Remote Exception: " + e);
+ }
+ return 0;
+ }
+
+ private int runGetSystemGrammaticalGender() {
+ int userId = ActivityManager.getCurrentUser();
+ do {
+ String option = getNextOption();
+ if (option == null) {
+ break;
+ }
+ switch (option) {
+ case "--user": {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ break;
+ }
+ default: {
+ throw new IllegalArgumentException("Unknown option: " + option);
+ }
+ }
+ } while (true);
+
+ try {
+ int grammaticalGender = mBinderService.getSystemGrammaticalGender(userId);
+ getOutPrintWriter().println(GRAMMATICAL_GENDER_MAP.get(grammaticalGender));
+ } catch (RemoteException e) {
+ getOutPrintWriter().println("Remote Exception: " + e);
+ }
+ return 0;
+ }
+
+ private int parseGrammaticalGender() {
+ String arg = getNextArg();
+ if (arg == null) {
+ return GRAMMATICAL_GENDER_NOT_SPECIFIED;
+ } else {
+ int grammaticalGender = Integer.parseInt(arg);
+ if (GrammaticalInflectionManager.VALID_GRAMMATICAL_GENDER_VALUES.contains(
+ grammaticalGender)) {
+ return grammaticalGender;
+ } else {
+ return GRAMMATICAL_GENDER_NOT_SPECIFIED;
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/grammaticalinflection/OWNERS b/services/core/java/com/android/server/grammaticalinflection/OWNERS
index 5f16ba9..41d079e 100644
--- a/services/core/java/com/android/server/grammaticalinflection/OWNERS
+++ b/services/core/java/com/android/server/grammaticalinflection/OWNERS
@@ -2,3 +2,4 @@
allenwtsu@google.com
goldmanj@google.com
calvinpan@google.com
+zoeychen@google.com
diff --git a/services/core/java/com/android/server/lights/TEST_MAPPING b/services/core/java/com/android/server/lights/TEST_MAPPING
index f868ea0..17b98ce8 100644
--- a/services/core/java/com/android/server/lights/TEST_MAPPING
+++ b/services/core/java/com/android/server/lights/TEST_MAPPING
@@ -4,16 +4,14 @@
"name": "CtsHardwareTestCases",
"options": [
{"include-filter": "com.android.hardware.lights"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
- {"exclude-annotation": "androidx.test.filters.LargeTest"},
- {"exclude-annotation": "androidx.test.filters.FlakyTest"}
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.LargeTest"}
]
},
{
"name": "FrameworksServicesTests",
"options": [
{"include-filter": "com.android.server.lights"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
}
diff --git a/services/core/java/com/android/server/locksettings/TEST_MAPPING b/services/core/java/com/android/server/locksettings/TEST_MAPPING
index b881b44..ddf3d76 100644
--- a/services/core/java/com/android/server/locksettings/TEST_MAPPING
+++ b/services/core/java/com/android/server/locksettings/TEST_MAPPING
@@ -7,7 +7,7 @@
"include-annotation": "com.android.cts.devicepolicy.annotations.LockSettingsTest"
},
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
@@ -20,7 +20,7 @@
"include-filter": "com.android.server.locksettings."
},
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
diff --git a/services/core/java/com/android/server/logcat/TEST_MAPPING b/services/core/java/com/android/server/logcat/TEST_MAPPING
index f4b13a0..9041552 100644
--- a/services/core/java/com/android/server/logcat/TEST_MAPPING
+++ b/services/core/java/com/android/server/logcat/TEST_MAPPING
@@ -4,7 +4,6 @@
"name": "FrameworksServicesTests",
"options": [
{"include-filter": "com.android.server.logcat"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
}
diff --git a/services/core/java/com/android/server/media/projection/TEST_MAPPING b/services/core/java/com/android/server/media/projection/TEST_MAPPING
index a792498..7aa9118 100644
--- a/services/core/java/com/android/server/media/projection/TEST_MAPPING
+++ b/services/core/java/com/android/server/media/projection/TEST_MAPPING
@@ -4,9 +4,6 @@
"name": "MediaProjectionTests",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
- },
- {
"exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
diff --git a/services/core/java/com/android/server/notification/TEST_MAPPING b/services/core/java/com/android/server/notification/TEST_MAPPING
index 7db2e8b..468c451 100644
--- a/services/core/java/com/android/server/notification/TEST_MAPPING
+++ b/services/core/java/com/android/server/notification/TEST_MAPPING
@@ -4,9 +4,6 @@
"name": "CtsNotificationTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
- },
- {
"exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
@@ -14,9 +11,6 @@
},
{
"exclude-annotation": "androidx.test.filters.LargeTest"
- },
- {
- "exclude-annotation": "androidx.test.filters.LargeTest"
}
]
},
@@ -24,9 +18,6 @@
"name": "FrameworksUiServicesTests",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
- },
- {
"exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
@@ -34,9 +25,6 @@
},
{
"exclude-annotation": "androidx.test.filters.LargeTest"
- },
- {
- "exclude-annotation": "androidx.test.filters.LargeTest"
}
]
}
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 390c45b..0fd7a37 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -55,6 +55,7 @@
},
{
"name": "GtsContentTestCases",
+ "keywords": ["internal"],
"options": [
{
"include-filter": "com.google.android.content.gts"
diff --git a/services/core/java/com/android/server/policy/TEST_MAPPING b/services/core/java/com/android/server/policy/TEST_MAPPING
index 819a82c..338b479 100644
--- a/services/core/java/com/android/server/policy/TEST_MAPPING
+++ b/services/core/java/com/android/server/policy/TEST_MAPPING
@@ -32,7 +32,7 @@
"name": "CtsPermissionPolicyTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
"include-filter": "android.permissionpolicy.cts.RestrictedPermissionsTest"
@@ -49,7 +49,7 @@
"name": "CtsPermissionTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
"include-filter": "android.permission.cts.SplitPermissionTest"
diff --git a/services/core/java/com/android/server/power/TEST_MAPPING b/services/core/java/com/android/server/power/TEST_MAPPING
index 19086a1..05a0e85 100644
--- a/services/core/java/com/android/server/power/TEST_MAPPING
+++ b/services/core/java/com/android/server/power/TEST_MAPPING
@@ -3,16 +3,14 @@
{
"name": "CtsBatterySavingTestCases",
"options": [
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
- {"exclude-annotation": "androidx.test.filters.LargeTest"},
- {"exclude-annotation": "androidx.test.filters.FlakyTest"}
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.LargeTest"}
]
},
{
"name": "FrameworksMockingServicesTests",
"options": [
{"include-filter": "com.android.server.power"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
},
@@ -20,7 +18,6 @@
"name": "PowerServiceTests",
"options": [
{"include-filter": "com.android.server.power"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "org.junit.Ignore"}
]
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 1684724..ed10346 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -5366,7 +5366,7 @@
// Finish should only ever commit visibility=false, so we can check full containment
// rather than just direct membership.
inFinishingTransition = mTransitionController.inFinishingTransition(this);
- if (!inFinishingTransition && !mDisplayContent.isSleeping()) {
+ if (!inFinishingTransition && (visible || !mDisplayContent.isSleeping())) {
Slog.e(TAG, "setVisibility=" + visible
+ " while transition is not collecting or finishing "
+ this + " caller=" + Debug.getCallers(8));
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 5d95bc7..50bc825 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -255,6 +255,11 @@
boolean mClearedForReorderActivityToFront;
/**
+ * Whether the TaskFragment surface is managed by a system {@link TaskFragmentOrganizer}.
+ */
+ boolean mIsSurfaceManagedBySystemOrganizer = false;
+
+ /**
* When we are in the process of pausing an activity, before starting the
* next one, this variable holds the activity that is currently being paused.
*
@@ -449,13 +454,21 @@
void setTaskFragmentOrganizer(@NonNull TaskFragmentOrganizerToken organizer, int uid,
@NonNull String processName) {
+ setTaskFragmentOrganizer(organizer, uid, processName,
+ false /* isSurfaceManagedBySystemOrganizer */);
+ }
+
+ void setTaskFragmentOrganizer(@NonNull TaskFragmentOrganizerToken organizer, int uid,
+ @NonNull String processName, boolean isSurfaceManagedBySystemOrganizer) {
mTaskFragmentOrganizer = ITaskFragmentOrganizer.Stub.asInterface(organizer.asBinder());
mTaskFragmentOrganizerUid = uid;
mTaskFragmentOrganizerProcessName = processName;
+ mIsSurfaceManagedBySystemOrganizer = isSurfaceManagedBySystemOrganizer;
}
void onTaskFragmentOrganizerRemoved() {
mTaskFragmentOrganizer = null;
+ mIsSurfaceManagedBySystemOrganizer = false;
}
/** Whether this TaskFragment is organized by the given {@code organizer}. */
@@ -2396,6 +2409,9 @@
if (mDelayOrganizedTaskFragmentSurfaceUpdate || mTaskFragmentOrganizer == null) {
return;
}
+ if (mIsSurfaceManagedBySystemOrganizer) {
+ return;
+ }
if (mTransitionController.isShellTransitionsEnabled()
&& !mTransitionController.isCollecting(this)) {
// TaskFragmentOrganizer doesn't have access to the surface for security reasons, so
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index ea722b6..04164c2 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -26,6 +26,7 @@
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
+import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission;
import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
import static com.android.server.wm.WindowOrganizerController.configurationsAreEqualForOrganizer;
@@ -50,12 +51,15 @@
import android.window.ITaskFragmentOrganizerController;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentOperation;
+import android.window.TaskFragmentOrganizerToken;
import android.window.TaskFragmentParentInfo;
import android.window.TaskFragmentTransaction;
import android.window.WindowContainerTransaction;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLogGroup;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.window.flags.Flags;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -133,6 +137,13 @@
new WeakHashMap<>();
/**
+ * Whether this {@link android.window.TaskFragmentOrganizer} is a system organizer. If true,
+ * the {@link android.view.SurfaceControl} of the {@link TaskFragment} is provided to the
+ * client in the {@link TYPE_TASK_FRAGMENT_APPEARED} event.
+ */
+ private final boolean mIsSystemOrganizer;
+
+ /**
* {@link RemoteAnimationDefinition} for embedded activities transition animation that is
* organized by this organizer.
*/
@@ -147,10 +158,12 @@
*/
private final ArrayMap<IBinder, Integer> mDeferredTransitions = new ArrayMap<>();
- TaskFragmentOrganizerState(ITaskFragmentOrganizer organizer, int pid, int uid) {
+ TaskFragmentOrganizerState(@NonNull ITaskFragmentOrganizer organizer, int pid, int uid,
+ boolean isSystemOrganizer) {
mOrganizer = organizer;
mOrganizerPid = pid;
mOrganizerUid = uid;
+ mIsSystemOrganizer = isSystemOrganizer;
try {
mOrganizer.asBinder().linkToDeath(this, 0 /*flags*/);
} catch (RemoteException e) {
@@ -235,11 +248,15 @@
tf.mTaskFragmentAppearedSent = true;
mLastSentTaskFragmentInfos.put(tf, info);
mTaskFragmentTaskIds.put(tf, taskId);
- return new TaskFragmentTransaction.Change(
+ final TaskFragmentTransaction.Change change = new TaskFragmentTransaction.Change(
TYPE_TASK_FRAGMENT_APPEARED)
.setTaskFragmentToken(tf.getFragmentToken())
.setTaskFragmentInfo(info)
.setTaskId(taskId);
+ if (mIsSystemOrganizer) {
+ change.setTaskFragmentSurfaceControl(tf.getSurfaceControl());
+ }
+ return change;
}
@NonNull
@@ -435,8 +452,25 @@
: null;
}
+ @VisibleForTesting
+ void registerOrganizer(@NonNull ITaskFragmentOrganizer organizer) {
+ registerOrganizerInternal(organizer, false /* isSystemOrganizer */);
+ }
+
@Override
- public void registerOrganizer(@NonNull ITaskFragmentOrganizer organizer) {
+ public void registerOrganizer(
+ @NonNull ITaskFragmentOrganizer organizer, boolean isSystemOrganizer) {
+ registerOrganizerInternal(
+ organizer,
+ Flags.taskFragmentSystemOrganizerFlag() && isSystemOrganizer);
+ }
+
+ @VisibleForTesting
+ void registerOrganizerInternal(
+ @NonNull ITaskFragmentOrganizer organizer, boolean isSystemOrganizer) {
+ if (isSystemOrganizer) {
+ enforceTaskPermission("registerSystemOrganizer()");
+ }
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
synchronized (mGlobalLock) {
@@ -448,7 +482,7 @@
"Replacing existing organizer currently unsupported");
}
mTaskFragmentOrganizerState.put(organizer.asBinder(),
- new TaskFragmentOrganizerState(organizer, pid, uid));
+ new TaskFragmentOrganizerState(organizer, pid, uid, isSystemOrganizer));
mPendingTaskFragmentEvents.put(organizer.asBinder(), new ArrayList<>());
}
}
@@ -711,6 +745,12 @@
}
}
+ boolean isSystemOrganizer(@NonNull TaskFragmentOrganizerToken token) {
+ final TaskFragmentOrganizerState state =
+ mTaskFragmentOrganizerState.get(token.asBinder());
+ return state != null && state.mIsSystemOrganizer;
+ }
+
@Nullable
private PendingTaskFragmentEvent getLastPendingParentInfoChangedEvent(
@NonNull ITaskFragmentOrganizer organizer, @NonNull Task task) {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 6d7e297..376cad7 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -99,6 +99,7 @@
import android.window.TaskFragmentAnimationParams;
import android.window.TaskFragmentCreationParams;
import android.window.TaskFragmentOperation;
+import android.window.TaskFragmentOrganizerToken;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -1993,8 +1994,10 @@
creationParams.getFragmentToken(), true /* createdByOrganizer */);
// Set task fragment organizer immediately, since it might have to be notified about further
// actions.
- taskFragment.setTaskFragmentOrganizer(creationParams.getOrganizer(),
- ownerActivity.getUid(), ownerActivity.info.processName);
+ TaskFragmentOrganizerToken organizerToken = creationParams.getOrganizer();
+ taskFragment.setTaskFragmentOrganizer(organizerToken,
+ ownerActivity.getUid(), ownerActivity.info.processName,
+ mTaskFragmentOrganizerController.isSystemOrganizer(organizerToken));
final int position;
if (creationParams.getPairedPrimaryFragmentToken() != null) {
// When there is a paired primary TaskFragment, we want to place the new TaskFragment
diff --git a/services/core/jni/TEST_MAPPING b/services/core/jni/TEST_MAPPING
index eb9db70..7f7eb48 100644
--- a/services/core/jni/TEST_MAPPING
+++ b/services/core/jni/TEST_MAPPING
@@ -7,7 +7,6 @@
"name": "CtsVibratorTestCases",
"options": [
{"exclude-annotation": "androidx.test.filters.LargeTest"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "org.junit.Ignore"}
]
diff --git a/services/devicepolicy/TEST_MAPPING b/services/devicepolicy/TEST_MAPPING
index fccd1ec..0d5534b 100644
--- a/services/devicepolicy/TEST_MAPPING
+++ b/services/devicepolicy/TEST_MAPPING
@@ -4,7 +4,7 @@
"name": "CtsDevicePolicyManagerTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
"exclude-annotation": "androidx.test.filters.LargeTest"
diff --git a/services/tests/InputMethodSystemServerTests/TEST_MAPPING b/services/tests/InputMethodSystemServerTests/TEST_MAPPING
index cedbfd2b..de9f771 100644
--- a/services/tests/InputMethodSystemServerTests/TEST_MAPPING
+++ b/services/tests/InputMethodSystemServerTests/TEST_MAPPING
@@ -4,7 +4,6 @@
"name": "FrameworksInputMethodSystemServerTests",
"options": [
{"include-filter": "com.android.server.inputmethod"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "org.junit.Ignore"}
]
@@ -15,7 +14,6 @@
"name": "FrameworksImeTests",
"options": [
{"include-filter": "com.android.inputmethodservice"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "org.junit.Ignore"}
]
diff --git a/services/tests/dreamservicetests/TEST_MAPPING b/services/tests/dreamservicetests/TEST_MAPPING
index d73d83d..a644ea6 100644
--- a/services/tests/dreamservicetests/TEST_MAPPING
+++ b/services/tests/dreamservicetests/TEST_MAPPING
@@ -4,7 +4,6 @@
"name": "DreamServiceTests",
"options": [
{"include-filter": "com.android.server.dreams"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "org.junit.Ignore"}
]
diff --git a/services/tests/powerstatstests/TEST_MAPPING b/services/tests/powerstatstests/TEST_MAPPING
index e1eb1e4..eee68a4 100644
--- a/services/tests/powerstatstests/TEST_MAPPING
+++ b/services/tests/powerstatstests/TEST_MAPPING
@@ -4,7 +4,6 @@
"name": "PowerStatsTests",
"options": [
{"include-filter": "com.android.server.power.stats"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"},
{"exclude-annotation": "org.junit.Ignore"}
]
diff --git a/services/tests/servicestests/src/com/android/server/recoverysystem/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/recoverysystem/TEST_MAPPING
index 4d1e405..e9d8b2e 100644
--- a/services/tests/servicestests/src/com/android/server/recoverysystem/TEST_MAPPING
+++ b/services/tests/servicestests/src/com/android/server/recoverysystem/TEST_MAPPING
@@ -7,7 +7,7 @@
"include-filter": "com.android.server.recoverysystem."
},
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index bfa279d..2bf1385 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -212,7 +212,30 @@
mController.dispatchPendingEvents();
assertTaskFragmentParentInfoChangedTransaction(mTask);
- assertTaskFragmentAppearedTransaction();
+ assertTaskFragmentAppearedTransaction(false /* hasSurfaceControl */);
+ }
+
+ @Test
+ public void testOnTaskFragmentAppeared_systemOrganizer() {
+ mController.unregisterOrganizer(mIOrganizer);
+ mController.registerOrganizerInternal(mIOrganizer, true /* isSystemOrganizer */);
+
+ // No-op when the TaskFragment is not attached.
+ mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+ mController.dispatchPendingEvents();
+
+ verify(mOrganizer, never()).onTransactionReady(any());
+
+ // Send callback when the TaskFragment is attached.
+ setupMockParent(mTaskFragment, mTask);
+
+ mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+ mController.dispatchPendingEvents();
+
+ assertTaskFragmentParentInfoChangedTransaction(mTask);
+
+ // System organizer should receive the SurfaceControl
+ assertTaskFragmentAppearedTransaction(true /* hasSurfaceControl */);
}
@Test
@@ -1664,7 +1687,7 @@
}
/** Asserts that there will be a transaction for TaskFragment appeared. */
- private void assertTaskFragmentAppearedTransaction() {
+ private void assertTaskFragmentAppearedTransaction(boolean hasSurfaceControl) {
verify(mOrganizer).onTransactionReady(mTransactionCaptor.capture());
final TaskFragmentTransaction transaction = mTransactionCaptor.getValue();
final List<TaskFragmentTransaction.Change> changes = transaction.getChanges();
@@ -1675,6 +1698,11 @@
assertEquals(TYPE_TASK_FRAGMENT_APPEARED, change.getType());
assertEquals(mTaskFragmentInfo, change.getTaskFragmentInfo());
assertEquals(mFragmentToken, change.getTaskFragmentToken());
+ if (hasSurfaceControl) {
+ assertNotNull(change.getTaskFragmentSurfaceControl());
+ } else {
+ assertNull(change.getTaskFragmentSurfaceControl());
+ }
}
/** Asserts that there will be a transaction for TaskFragment info changed. */
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 6a9bb6c..5205bb0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -27,8 +27,12 @@
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyFloat;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.clearInvocations;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -124,6 +128,17 @@
}
@Test
+ public void testUpdateOrganizedTaskFragmentSurface_noSurfaceUpdateWhenOrganizedBySystem() {
+ clearInvocations(mTransaction);
+ mTaskFragment.mIsSurfaceManagedBySystemOrganizer = true;
+
+ mTaskFragment.updateOrganizedTaskFragmentSurface();
+
+ verify(mTransaction, never()).setPosition(eq(mLeash), anyFloat(), anyFloat());
+ verify(mTransaction, never()).setWindowCrop(eq(mLeash), anyInt(), anyInt());
+ }
+
+ @Test
public void testShouldStartChangeTransition_relativePositionChange() {
final Task task = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW,
ACTIVITY_TYPE_STANDARD);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 36a8fc1..a5e0638 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -18,6 +18,7 @@
import android.Manifest;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -52,6 +53,7 @@
import android.telephony.ims.feature.RcsFeature;
import com.android.internal.telephony.ICarrierConfigLoader;
+import com.android.internal.telephony.flags.Flags;
import com.android.telephony.Rlog;
import java.util.List;
@@ -9436,22 +9438,9 @@
* </carrier_config>
* }</pre>
* <p>
- * If this carrier config is not present, the device overlay config
- * {@code config_satellite_services_supported_by_providers} will be used. If the carrier config
- * is present, the supported services associated with the PLMNs listed in the carrier config
- * will override that of the device overlay config. The supported satellite services will be
- * identified as follows:
- * <ul>
- * <li>For each PLMN that exists only in the carrier provided satellite services, use the
- * carrier provided services as the supported services.</li>
- * <li>For each PLMN that is present only in the device provided satellite services, use the
- * device provided services as the supported services.</li>
- * <li>For each PLMN that is present in both the carrier provided and device provided satellite
- * services, use the carrier provided services as the supported services.</li>
- * </ul>
- * <p>
* This config is empty by default.
*/
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
public static final String KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE =
"carrier_supported_satellite_services_per_provider_bundle";
@@ -9462,9 +9451,8 @@
* satellite provider and the carrier before enabling this flag.
*
* The default value is false.
- *
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
public static final String KEY_SATELLITE_ATTACH_SUPPORTED_BOOL =
"satellite_attach_supported_bool";
@@ -10430,8 +10418,12 @@
sDefaults.putAll(Iwlan.getDefaults());
sDefaults.putStringArray(KEY_CARRIER_CERTIFICATE_STRING_ARRAY, new String[0]);
sDefaults.putBoolean(KEY_FORMAT_INCOMING_NUMBER_TO_NATIONAL_FOR_JP_BOOL, false);
- sDefaults.putIntArray(KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY,
- new int[] {4 /* BUSY */});
+ if (Flags.doNotOverridePreciseLabel()) {
+ sDefaults.putIntArray(KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY, new int[]{});
+ } else {
+ sDefaults.putIntArray(KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY,
+ new int[]{4 /* BUSY */});
+ }
sDefaults.putBoolean(KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL, false);
sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG, 5000);
sDefaults.putStringArray(KEY_MMI_TWO_DIGIT_NUMBER_PATTERN_STRING_ARRAY, new String[0]);
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index a4527f12..8dc2de8 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -85,29 +85,11 @@
@Nullable private final Context mContext;
/**
- * Create a new SatelliteManager object pinned to the given subscription ID.
- * This is needed only to handle carrier specific satellite features.
- * For eg: requestSatelliteAttachEnabledForCarrier and
- * requestIsSatelliteAttachEnabledForCarrier
- *
- * @return a SatelliteManager that uses the given subId for all satellite activities.
- * @throws IllegalArgumentException if the subscription is invalid.
- * @hide
- */
- public SatelliteManager createForSubscriptionId(int subId) {
- if (!SubscriptionManager.isValidSubscriptionId(subId)) {
- throw new IllegalArgumentException("Invalid subscription ID");
- }
- return new SatelliteManager(mContext, subId);
- }
-
- /**
* Create an instance of the SatelliteManager.
*
* @param context The context the SatelliteManager belongs to.
* @hide
*/
-
public SatelliteManager(@Nullable Context context) {
this(context, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
}
@@ -846,8 +828,8 @@
/**
* Satellite communication restricted by geolocation. This can be
* triggered based upon geofence input provided by carrier to enable or disable satellite.
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
public static final int SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION = 1;
/** @hide */
@@ -1643,26 +1625,28 @@
* {@code true}.</li>
* </ul>
*
+ * @param subId The subscription ID of the carrier.
* @param enableSatellite {@code true} to enable the satellite and {@code false} to disable.
* @param executor The executor on which the error code listener will be called.
* @param resultListener Listener for the {@link SatelliteError} result of the operation.
*
* @throws SecurityException if the caller doesn't have required permission.
* @throws IllegalStateException if the Telephony process is not currently available.
- * @hide
+ * @throws IllegalArgumentException if the subscription is invalid.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- public void requestSatelliteAttachEnabledForCarrier(boolean enableSatellite,
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+ public void requestSatelliteAttachEnabledForCarrier(int subId, boolean enableSatellite,
@NonNull @CallbackExecutor Executor executor,
@SatelliteResult @NonNull Consumer<Integer> resultListener) {
Objects.requireNonNull(executor);
Objects.requireNonNull(resultListener);
if (enableSatellite) {
- removeSatelliteAttachRestrictionForCarrier(
+ removeSatelliteAttachRestrictionForCarrier(subId,
SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER, executor, resultListener);
} else {
- addSatelliteAttachRestrictionForCarrier(
+ addSatelliteAttachRestrictionForCarrier(subId,
SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER, executor, resultListener);
}
}
@@ -1671,6 +1655,7 @@
* Request to get whether the carrier supported satellite plmn scan and attach by modem is
* enabled by user.
*
+ * @param subId The subscription ID of the carrier.
* @param executor The executor on which the callback will be called.
* @param callback The callback object to which the result will be delivered.
* If the request is successful, {@link OutcomeReceiver#onResult(Object)}
@@ -1681,16 +1666,17 @@
*
* @throws SecurityException if the caller doesn't have required permission.
* @throws IllegalStateException if the Telephony process is not currently available.
- * @hide
+ * @throws IllegalArgumentException if the subscription is invalid.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- public void requestIsSatelliteAttachEnabledForCarrier(
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+ public void requestIsSatelliteAttachEnabledForCarrier(int subId,
@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
Objects.requireNonNull(executor);
Objects.requireNonNull(callback);
- Set<Integer> restrictionReason = getSatelliteAttachRestrictionReasonsForCarrier();
+ Set<Integer> restrictionReason = getSatelliteAttachRestrictionReasonsForCarrier(subId);
executor.execute(() -> callback.onResult(
!restrictionReason.contains(SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER)));
}
@@ -1699,19 +1685,25 @@
* Add a restriction reason for disallowing carrier supported satellite plmn scan and attach
* by modem.
*
+ * @param subId The subscription ID of the carrier.
* @param reason Reason for disallowing satellite communication.
* @param executor The executor on which the error code listener will be called.
* @param resultListener Listener for the {@link SatelliteError} result of the operation.
*
* @throws SecurityException if the caller doesn't have required permission.
* @throws IllegalStateException if the Telephony process is not currently available.
- * @hide
+ * @throws IllegalArgumentException if the subscription is invalid.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- public void addSatelliteAttachRestrictionForCarrier(
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+ public void addSatelliteAttachRestrictionForCarrier(int subId,
@SatelliteCommunicationRestrictionReason int reason,
@NonNull @CallbackExecutor Executor executor,
@SatelliteResult @NonNull Consumer<Integer> resultListener) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ throw new IllegalArgumentException("Invalid subscription ID");
+ }
+
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
@@ -1722,7 +1714,7 @@
() -> resultListener.accept(result)));
}
};
- telephony.addSatelliteAttachRestrictionForCarrier(mSubId, reason, errorCallback);
+ telephony.addSatelliteAttachRestrictionForCarrier(subId, reason, errorCallback);
} else {
throw new IllegalStateException("telephony service is null.");
}
@@ -1736,19 +1728,25 @@
* Remove a restriction reason for disallowing carrier supported satellite plmn scan and attach
* by modem.
*
+ * @param subId The subscription ID of the carrier.
* @param reason Reason for disallowing satellite communication.
* @param executor The executor on which the error code listener will be called.
* @param resultListener Listener for the {@link SatelliteError} result of the operation.
*
* @throws SecurityException if the caller doesn't have required permission.
* @throws IllegalStateException if the Telephony process is not currently available.
- * @hide
+ * @throws IllegalArgumentException if the subscription is invalid.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- public void removeSatelliteAttachRestrictionForCarrier(
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+ public void removeSatelliteAttachRestrictionForCarrier(int subId,
@SatelliteCommunicationRestrictionReason int reason,
@NonNull @CallbackExecutor Executor executor,
@SatelliteResult @NonNull Consumer<Integer> resultListener) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ throw new IllegalArgumentException("Invalid subscription ID");
+ }
+
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
@@ -1759,7 +1757,7 @@
() -> resultListener.accept(result)));
}
};
- telephony.removeSatelliteAttachRestrictionForCarrier(mSubId, reason, errorCallback);
+ telephony.removeSatelliteAttachRestrictionForCarrier(subId, reason, errorCallback);
} else {
throw new IllegalStateException("telephony service is null.");
}
@@ -1773,34 +1771,40 @@
* Get reasons for disallowing satellite attach, as requested by
* {@link #addSatelliteAttachRestrictionForCarrier(int, Executor, Consumer)}
*
+ * @param subId The subscription ID of the carrier.
* @return Set of reasons for disallowing satellite communication.
*
* @throws SecurityException if the caller doesn't have required permission.
* @throws IllegalStateException if the Telephony process is not currently available.
- * @hide
+ * @throws IllegalArgumentException if the subscription is invalid.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
@SatelliteCommunicationRestrictionReason
- public @NonNull Set<Integer> getSatelliteAttachRestrictionReasonsForCarrier() {
+ @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+ @NonNull public Set<Integer> getSatelliteAttachRestrictionReasonsForCarrier(int subId) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ throw new IllegalArgumentException("Invalid subscription ID");
+ }
+
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
int[] receivedArray =
- telephony.getSatelliteAttachRestrictionReasonsForCarrier(mSubId);
+ telephony.getSatelliteAttachRestrictionReasonsForCarrier(subId);
if (receivedArray.length == 0) {
- logd("received set is empty, create empty set");
+ logd("receivedArray is empty, create empty set");
return new HashSet<>();
} else {
return Arrays.stream(receivedArray).boxed().collect(Collectors.toSet());
}
} else {
- throw new IllegalStateException("telephony service is null.");
+ throw new IllegalStateException("Telephony service is null.");
}
} catch (RemoteException ex) {
loge("getSatelliteAttachRestrictionReasonsForCarrier() RemoteException: " + ex);
ex.rethrowFromSystemServer();
}
- return null;
+ return new HashSet<>();
}
private static ITelephony getITelephony() {
diff --git a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
index 02661de..0fcd0d6 100644
--- a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
+++ b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
@@ -392,7 +392,11 @@
*
* @param simSlot Indicates the SIM slot to which this API will be applied. The modem will use
* this information to determine the relevant carrier.
- * @param plmnList The list of roaming PLMN used for connecting to satellite networks.
+ * @param carrierPlmnList The list of roaming PLMN used for connecting to satellite networks
+ * supported by user subscription.
+ * @param allSatellitePlmnList Modem should use the allSatellitePlmnList to identify satellite
+ * PLMNs that are not supported by the carrier and make sure not to
+ * attach to them.
* @param resultCallback The callback to receive the error code result of the operation.
*
* Valid error codes returned:
@@ -404,8 +408,8 @@
* SatelliteError:RADIO_NOT_AVAILABLE
* SatelliteError:REQUEST_NOT_SUPPORTED
*/
- void setSatellitePlmn(int simSlot, in List<String> plmnList,
- in IIntegerConsumer resultCallback);
+ void setSatellitePlmn(int simSlot, in List<String> carrierPlmnList,
+ in List<String> allSatellitePlmnList, in IIntegerConsumer resultCallback);
/**
* Enable or disable satellite in the cellular modem associated with a carrier.
diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
index 6451daf..a9c09c9 100644
--- a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
+++ b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
@@ -214,12 +214,12 @@
}
@Override
- public void setSatellitePlmn(int simSlot, List<String> plmnList,
- IIntegerConsumer errorCallback)
+ public void setSatellitePlmn(int simSlot, List<String> carrierPlmnList,
+ List<String> devicePlmnList, IIntegerConsumer errorCallback)
throws RemoteException {
executeMethodAsync(
- () -> SatelliteImplBase.this
- .setSatellitePlmn(simSlot, plmnList, errorCallback),
+ () -> SatelliteImplBase.this.setSatellitePlmn(
+ simSlot, carrierPlmnList, devicePlmnList, errorCallback),
"setSatellitePlmn");
}
@@ -655,13 +655,15 @@
* SIM profile. Acquisition of satellite based system is lower priority to terrestrial
* networks. UE shall make all attempts to acquire terrestrial service prior to camping on
* satellite LTE service.
- * This method must only take effect if {@link #setSatelliteEnabledForCarrier} is {@code true},
- * and return an error otherwise.
*
* @param simLogicalSlotIndex Indicates the SIM logical slot index to which this API will be
* applied. The modem will use this information to determine the relevant carrier.
* @param errorCallback The callback to receive the error code result of the operation.
- * @param plmnList The list of roaming PLMN used for connecting to satellite networks.
+ * @param carrierPlmnList The list of roaming PLMN used for connecting to satellite networks
+ * supported by user subscription.
+ * @param allSatellitePlmnList Modem should use the allSatellitePlmnList to identify satellite
+ * PLMNs that are not supported by the carrier and make sure not to
+ * attach to them.
*
* Valid error codes returned:
* SatelliteError:NONE
@@ -672,7 +674,8 @@
* SatelliteError:RADIO_NOT_AVAILABLE
* SatelliteError:REQUEST_NOT_SUPPORTED
*/
- public void setSatellitePlmn(@NonNull int simLogicalSlotIndex, @NonNull List<String> plmnList,
+ public void setSatellitePlmn(@NonNull int simLogicalSlotIndex,
+ @NonNull List<String> carrierPlmnList, @NonNull List<String> allSatellitePlmnList,
@NonNull IIntegerConsumer errorCallback) {
// stub implementation
}
diff --git a/tests/utils/testutils/TEST_MAPPING b/tests/utils/testutils/TEST_MAPPING
index 6468d8c..52fd5a8 100644
--- a/tests/utils/testutils/TEST_MAPPING
+++ b/tests/utils/testutils/TEST_MAPPING
@@ -7,9 +7,6 @@
"exclude-annotation": "androidx.test.filters.LargeTest"
},
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
- },
- {
"exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
index 95cbff9..d176b5e 100644
--- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
+++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
@@ -69,7 +69,8 @@
String methodDescriptor,
String callbackMethod
) {
- callStaticMethodByName(callbackMethod, methodClass, methodName, methodDescriptor);
+ callStaticMethodByName(callbackMethod, "method call hook", methodClass,
+ methodName, methodDescriptor);
}
/**
@@ -167,31 +168,38 @@
logPrintStream.println("! Class loaded: " + loadedClass.getCanonicalName()
+ " calling hook " + callbackMethod);
- callStaticMethodByName(callbackMethod, loadedClass);
+ callStaticMethodByName(callbackMethod, "class load hook", loadedClass);
}
- private static void callStaticMethodByName(String classAndMethodName, Object... args) {
+ private static void callStaticMethodByName(String classAndMethodName,
+ String description, Object... args) {
// Forward the call to callbackMethod.
final int lastPeriod = classAndMethodName.lastIndexOf(".");
- final String className = classAndMethodName.substring(0, lastPeriod);
- final String methodName = classAndMethodName.substring(lastPeriod + 1);
- if (lastPeriod < 0 || className.isEmpty() || methodName.isEmpty()) {
+ if ((lastPeriod) < 0 || (lastPeriod == classAndMethodName.length() - 1)) {
throw new HostTestException(String.format(
- "Unable to find class load hook: malformed method name \"%s\"",
+ "Unable to find %s: malformed method name \"%s\"",
+ description,
classAndMethodName));
}
+ final String className = classAndMethodName.substring(0, lastPeriod);
+ final String methodName = classAndMethodName.substring(lastPeriod + 1);
+
Class<?> clazz = null;
try {
clazz = Class.forName(className);
} catch (Exception e) {
throw new HostTestException(String.format(
- "Unable to find class load hook: Class %s not found", className), e);
+ "Unable to find %s: Class %s not found",
+ description,
+ className), e);
}
if (!Modifier.isPublic(clazz.getModifiers())) {
throw new HostTestException(String.format(
- "Unable to find class load hook: Class %s must be public", className));
+ "Unable to find %s: Class %s must be public",
+ description,
+ className));
}
Class<?>[] argTypes = new Class[args.length];
@@ -204,25 +212,23 @@
method = clazz.getMethod(methodName, argTypes);
} catch (Exception e) {
throw new HostTestException(String.format(
- "Unable to find class load hook: class %s doesn't have method %s"
+ "Unable to find %s: class %s doesn't have method %s"
+ " (method must take exactly one parameter of type Class,"
+ " and public static)",
- className,
- methodName), e);
+ description, className, methodName), e);
}
if (!(Modifier.isPublic(method.getModifiers())
&& Modifier.isStatic(method.getModifiers()))) {
throw new HostTestException(String.format(
- "Unable to find class load hook: Method %s in class %s must be public static",
- methodName, className));
+ "Unable to find %s: Method %s in class %s must be public static",
+ description, methodName, className));
}
try {
method.invoke(null, args);
} catch (Exception e) {
throw new HostTestException(String.format(
- "Unable to invoke class load hook %s.%s",
- className,
- methodName), e);
+ "Unable to invoke %s %s.%s",
+ description, className, methodName), e);
}
}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt
index 7d7852a..f75062b 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt
@@ -32,6 +32,10 @@
return uncommented.trim()
}
+/**
+ * Concatenate list [a] and [b] and return it. As an optimization, it returns an input
+ * [List] as-is if the other [List] is empty, so do not modify input [List]'s.
+ */
fun <T> addLists(a: List<T>, b: List<T>): List<T> {
if (a.isEmpty()) {
return b
@@ -42,6 +46,10 @@
return a + b
}
+/**
+ * Add element [b] to list [a] if [b] is not null. Otherwise, just return [a].
+ * (because the method may return [a] as-is, do not modify it after passing it.)
+ */
fun <T> addNonNullElement(a: List<T>, b: T?): List<T> {
if (b == null) {
return a