Merge "Remove VibrationXmlParser @TestApi annotations" into main
diff --git a/core/api/current.txt b/core/api/current.txt
index 5456c15..46f426b 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -12494,6 +12494,7 @@
method @FlaggedApi("android.os.allow_private_profile") @Nullable @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public final android.content.pm.LauncherUserInfo getLauncherUserInfo(@NonNull android.os.UserHandle);
method public android.content.pm.LauncherApps.PinItemRequest getPinItemRequest(android.content.Intent);
method @FlaggedApi("android.os.allow_private_profile") @NonNull @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public java.util.List<java.lang.String> getPreInstalledSystemPackages(@NonNull android.os.UserHandle);
+ method @FlaggedApi("android.os.get_private_space_settings") @Nullable @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public android.content.IntentSender getPrivateSpaceSettingsIntent();
method @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public java.util.List<android.os.UserHandle> getProfiles();
method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(android.content.pm.ShortcutInfo, int);
method @Nullable public android.content.IntentSender getShortcutConfigActivityIntent(@NonNull android.content.pm.LauncherActivityInfo);
@@ -20355,6 +20356,7 @@
method public android.view.Surface getSurface();
method public void release();
method public void resize(int, int, int);
+ method @FlaggedApi("android.companion.virtualdevice.flags.virtual_display_rotation_api") public void setRotation(int);
method public void setSurface(android.view.Surface);
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index abc2224..36a335e 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -5162,10 +5162,12 @@
}
public final class VirtualDisplayConfig implements android.os.Parcelable {
+ method @FlaggedApi("android.companion.virtualdevice.flags.virtual_display_insets") @Nullable public android.view.DisplayCutout getDisplayCutout();
method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") public boolean isHomeSupported();
}
public static final class VirtualDisplayConfig.Builder {
+ method @FlaggedApi("android.companion.virtualdevice.flags.virtual_display_insets") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setDisplayCutout(@Nullable android.view.DisplayCutout);
method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setHomeSupported(boolean);
}
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index ef8501f..aca9bb4 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -297,6 +297,12 @@
public boolean isTopActivityTransparent;
/**
+ * Whether the top activity has specified style floating.
+ * @hide
+ */
+ public boolean isTopActivityStyleFloating;
+
+ /**
* Encapsulate specific App Compat information.
* @hide
*/
@@ -429,6 +435,7 @@
&& parentTaskId == that.parentTaskId
&& Objects.equals(topActivity, that.topActivity)
&& isTopActivityTransparent == that.isTopActivityTransparent
+ && isTopActivityStyleFloating == that.isTopActivityStyleFloating
&& appCompatTaskInfo.equalsForTaskOrganizer(that.appCompatTaskInfo);
}
@@ -498,6 +505,7 @@
mTopActivityLocusId = source.readTypedObject(LocusId.CREATOR);
displayAreaFeatureId = source.readInt();
isTopActivityTransparent = source.readBoolean();
+ isTopActivityStyleFloating = source.readBoolean();
appCompatTaskInfo = source.readTypedObject(AppCompatTaskInfo.CREATOR);
}
@@ -545,6 +553,7 @@
dest.writeTypedObject(mTopActivityLocusId, flags);
dest.writeInt(displayAreaFeatureId);
dest.writeBoolean(isTopActivityTransparent);
+ dest.writeBoolean(isTopActivityStyleFloating);
dest.writeTypedObject(appCompatTaskInfo, flags);
}
@@ -582,6 +591,7 @@
+ " locusId=" + mTopActivityLocusId
+ " displayAreaFeatureId=" + displayAreaFeatureId
+ " isTopActivityTransparent=" + isTopActivityTransparent
+ + " isTopActivityStyleFloating=" + isTopActivityStyleFloating
+ " appCompatTaskInfo=" + appCompatTaskInfo
+ "}";
}
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index 64d2081..b63e2cf 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -32,6 +32,13 @@
}
flag {
+ namespace: "virtual_devices"
+ name: "virtual_display_insets"
+ description: "APIs for specifying virtual display insets (via cutout)"
+ bug: "350007135"
+}
+
+flag {
namespace: "virtual_devices"
name: "metrics_collection"
description: "Enable collection of VDM-related metrics"
@@ -89,3 +96,10 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ namespace: "virtual_devices"
+ name: "virtual_display_rotation_api"
+ description: "API for on-demand rotation of virtual displays"
+ bug: "291748430"
+}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index ab58f43..8365840 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2925,10 +2925,12 @@
*/
@SuppressWarnings("HiddenAbstractMethod")
@RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
- public abstract void sendOrderedBroadcastAsUserMultiplePermissions(Intent intent,
+ public void sendOrderedBroadcastAsUserMultiplePermissions(Intent intent,
UserHandle user, String[] receiverPermissions, int appOp, Bundle options,
BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
- String initialData, Bundle initialExtras);
+ String initialData, Bundle initialExtras) {
+ throw new RuntimeException("Not implemented. Must override in a subclass.");
+ }
/**
* Version of
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index df27f9d..52c84dc 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -935,12 +935,11 @@
*
* @return {@link IntentSender} object which launches the Private Space Settings Activity, if
* successful, null otherwise.
- * @hide
*/
// Alternatively, a system app can access this api for private profile if they've been granted
// with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
@Nullable
- @FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
+ @FlaggedApi(Flags.FLAG_GET_PRIVATE_SPACE_SETTINGS)
@RequiresPermission(conditional = true,
anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
public IntentSender getPrivateSpaceSettingsIntent() {
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 124d07f..c7d94c6 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -151,6 +151,16 @@
}
flag {
+ name: "fix_get_user_property_cache"
+ namespace: "multiuser"
+ description: "Cache is not optimised for getUserProperty for values below 0, eg. UserHandler.USER_NULL or UserHandle.USER_ALL"
+ bug: "350416200"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "cache_quiet_mode_state"
namespace: "multiuser"
description: "Optimise quiet mode state retrieval"
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 50d976f..4935389 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -1423,7 +1423,9 @@
* {@code false} otherwise.
* @throws UnsupportedOperationException if the query operation is not supported by the camera
* device
- * @throws IllegalArgumentException if the session configuration is invalid
+ * @throws IllegalArgumentException if the session configuration is invalid, including, if it
+ * contains certain non-supported features queryable via
+ * CameraCharacteristics.
* @throws CameraAccessException if the camera device is no longer connected or has
* encountered a fatal error
* @throws IllegalStateException if the camera device has been closed
@@ -1691,12 +1693,11 @@
*
* <p><b>IMPORTANT:</b></p>
* <ul>
- * <li>If feature support can be queried via
- * {@link CameraCharacteristics#SCALER_MANDATORY_STREAM_COMBINATIONS} or
- * {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP}, applications should
- * directly use that route rather than calling this function as: (1) using
- * {@code CameraCharacteristics} is more efficient, and (2) calling this function with
- * certain non-supported features will throw a {@link IllegalArgumentException}.</li>
+ * <li>If feature support can be queried via {@link CameraCharacteristics}, applications
+ * should directly use that route rather than calling this function as: (1) using
+ * {@code CameraCharacteristics} is more efficient, and (2) querying a feature explicitly
+ * deemed unsupported by CameraCharacteristics may throw a
+ * {@link IllegalArgumentException}.</li>
*
* <li>To minimize {@link SessionConfiguration} creation latency due to its dependency on
* output surfaces, the application can call this method before acquiring valid
@@ -1724,7 +1725,8 @@
*
* @throws CameraAccessException if the camera device is no longer connected or has
* encountered a fatal error
- * @throws IllegalArgumentException if the session configuration is invalid
+ * @throws IllegalArgumentException if the session configuration is invalid, including,
+ * if it contains certain non-supported features queryable via CameraCharacteristics.
*
* @see CameraCharacteristics#INFO_SESSION_CONFIGURATION_QUERY_VERSION
* @see SessionConfiguration
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 8748bb1..e9cd37a 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -817,6 +817,14 @@
}
}
+ void setVirtualDisplayRotation(IVirtualDisplayCallback token, @Surface.Rotation int rotation) {
+ try {
+ mDm.setVirtualDisplayRotation(token, rotation);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
/**
* Gets the stable device display size, in pixels.
*/
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 28c71d9..77277ee 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -117,6 +117,9 @@
// No permissions required but must be same Uid as the creator.
void setVirtualDisplayState(in IVirtualDisplayCallback token, boolean isOn);
+ // No permissions required but must be same Uid as the creator.
+ void setVirtualDisplayRotation(in IVirtualDisplayCallback token, int rotation);
+
// Get a stable metric for the device's display size. No permissions required.
Point getStableDisplaySize();
diff --git a/core/java/android/hardware/display/VirtualDisplay.java b/core/java/android/hardware/display/VirtualDisplay.java
index 051ce63..6cc938f 100644
--- a/core/java/android/hardware/display/VirtualDisplay.java
+++ b/core/java/android/hardware/display/VirtualDisplay.java
@@ -15,6 +15,7 @@
*/
package android.hardware.display;
+import android.annotation.FlaggedApi;
import android.view.Display;
import android.view.Surface;
@@ -122,6 +123,28 @@
}
}
+ /**
+ * Sets the rotation of the virtual display.
+ *
+ * @param rotation the new rotation of the display. May be one of {@link Surface#ROTATION_0},
+ * {@link Surface#ROTATION_90}, {@link Surface#ROTATION_180}, {@link Surface#ROTATION_270}.
+ * Upon creation, the rotation of the virtual display is always {@link Surface#ROTATION_0}.
+ */
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_VIRTUAL_DISPLAY_ROTATION_API)
+ public void setRotation(@Surface.Rotation int rotation) {
+ if (!android.companion.virtualdevice.flags.Flags.virtualDisplayRotationApi()) {
+ return;
+ }
+ if (rotation != Surface.ROTATION_0 && rotation != Surface.ROTATION_90
+ && rotation != Surface.ROTATION_180 && rotation != Surface.ROTATION_270) {
+ throw new IllegalArgumentException(
+ "Invalid virtual display rotation value: " + rotation);
+ }
+ if (mToken != null && mDisplay.getRotation() != rotation) {
+ mGlobal.setVirtualDisplayRotation(mToken, rotation);
+ }
+ }
+
@Override
public String toString() {
return "VirtualDisplay{display=" + mDisplay + ", token=" + mToken
diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java
index 56f69a6..b0994e6 100644
--- a/core/java/android/hardware/display/VirtualDisplayConfig.java
+++ b/core/java/android/hardware/display/VirtualDisplayConfig.java
@@ -31,6 +31,7 @@
import android.os.Parcelable;
import android.util.ArraySet;
import android.view.Display;
+import android.view.DisplayCutout;
import android.view.Surface;
import java.util.Collections;
@@ -55,9 +56,10 @@
private final String mUniqueId;
private final int mDisplayIdToMirror;
private final boolean mWindowManagerMirroringEnabled;
- private ArraySet<String> mDisplayCategories = null;
+ private final ArraySet<String> mDisplayCategories;
private final float mRequestedRefreshRate;
private final boolean mIsHomeSupported;
+ private final DisplayCutout mDisplayCutout;
private VirtualDisplayConfig(
@NonNull String name,
@@ -71,7 +73,8 @@
boolean windowManagerMirroringEnabled,
@NonNull ArraySet<String> displayCategories,
float requestedRefreshRate,
- boolean isHomeSupported) {
+ boolean isHomeSupported,
+ @Nullable DisplayCutout displayCutout) {
mName = name;
mWidth = width;
mHeight = height;
@@ -84,6 +87,7 @@
mDisplayCategories = displayCategories;
mRequestedRefreshRate = requestedRefreshRate;
mIsHomeSupported = isHomeSupported;
+ mDisplayCutout = displayCutout;
}
/**
@@ -135,6 +139,21 @@
}
/**
+ * Returns the cutout of this display.
+ *
+ * @return the cutout of the display or {@code null} if none is specified.
+ *
+ * @see Builder#setDisplayCutout
+ * @hide
+ */
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_VIRTUAL_DISPLAY_INSETS)
+ @SystemApi
+ @Nullable
+ public DisplayCutout getDisplayCutout() {
+ return mDisplayCutout;
+ }
+
+ /**
* Returns the unique identifier for the display. Shouldn't be displayed to the user.
* @hide
*/
@@ -207,6 +226,7 @@
dest.writeArraySet(mDisplayCategories);
dest.writeFloat(mRequestedRefreshRate);
dest.writeBoolean(mIsHomeSupported);
+ DisplayCutout.ParcelableWrapper.writeCutoutToParcel(mDisplayCutout, dest, flags);
}
@Override
@@ -232,7 +252,8 @@
&& mWindowManagerMirroringEnabled == that.mWindowManagerMirroringEnabled
&& Objects.equals(mDisplayCategories, that.mDisplayCategories)
&& mRequestedRefreshRate == that.mRequestedRefreshRate
- && mIsHomeSupported == that.mIsHomeSupported;
+ && mIsHomeSupported == that.mIsHomeSupported
+ && Objects.equals(mDisplayCutout, that.mDisplayCutout);
}
@Override
@@ -240,7 +261,7 @@
int hashCode = Objects.hash(
mName, mWidth, mHeight, mDensityDpi, mFlags, mSurface, mUniqueId,
mDisplayIdToMirror, mWindowManagerMirroringEnabled, mDisplayCategories,
- mRequestedRefreshRate, mIsHomeSupported);
+ mRequestedRefreshRate, mIsHomeSupported, mDisplayCutout);
return hashCode;
}
@@ -260,6 +281,7 @@
+ " mDisplayCategories=" + mDisplayCategories
+ " mRequestedRefreshRate=" + mRequestedRefreshRate
+ " mIsHomeSupported=" + mIsHomeSupported
+ + " mDisplayCutout=" + mDisplayCutout
+ ")";
}
@@ -276,6 +298,7 @@
mDisplayCategories = (ArraySet<String>) in.readArraySet(null);
mRequestedRefreshRate = in.readFloat();
mIsHomeSupported = in.readBoolean();
+ mDisplayCutout = DisplayCutout.ParcelableWrapper.readCutoutFromParcel(in);
}
@NonNull
@@ -308,6 +331,7 @@
private ArraySet<String> mDisplayCategories = new ArraySet<>();
private float mRequestedRefreshRate = 0.0f;
private boolean mIsHomeSupported = false;
+ private DisplayCutout mDisplayCutout = null;
/**
* Creates a new Builder.
@@ -469,6 +493,19 @@
}
/**
+ * Sets the cutout of this display.
+ *
+ * @hide
+ */
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_VIRTUAL_DISPLAY_INSETS)
+ @SystemApi
+ @NonNull
+ public Builder setDisplayCutout(@Nullable DisplayCutout displayCutout) {
+ mDisplayCutout = displayCutout;
+ return this;
+ }
+
+ /**
* Builds the {@link VirtualDisplayConfig} instance.
*/
@NonNull
@@ -485,7 +522,8 @@
mWindowManagerMirroringEnabled,
mDisplayCategories,
mRequestedRefreshRate,
- mIsHomeSupported);
+ mIsHomeSupported,
+ mDisplayCutout);
}
}
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 2ded615..903e916 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -699,6 +699,25 @@
}
/**
+ * Set whether the HAL should ignore display touches.
+ * Only applies to sensors where the HAL is reponsible for handling touches.
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public void setIgnoreDisplayTouches(long requestId, int sensorId, boolean ignoreTouch) {
+ if (mService == null) {
+ Slog.w(TAG, "setIgnoreDisplayTouches: no fingerprint service");
+ return;
+ }
+
+ try {
+ mService.setIgnoreDisplayTouches(requestId, sensorId, ignoreTouch);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Request fingerprint enrollment. This call warms up the fingerprint hardware
* and starts scanning for fingerprints. Progress will be indicated by callbacks to the
* {@link EnrollmentCallback} object. It terminates when
diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
index f701ec3..d84d292 100644
--- a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
+++ b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
@@ -120,6 +120,14 @@
}
/**
+ * Returns if sensor type is ultrasonic Udfps
+ * @return true if sensor is ultrasonic Udfps, false otherwise
+ */
+ public boolean isUltrasonicUdfps() {
+ return sensorType == TYPE_UDFPS_ULTRASONIC;
+ }
+
+ /**
* Returns if sensor type is side-FPS
* @return true if sensor is side-fps, false otherwise
*/
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 742fa57..370f097 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -195,6 +195,9 @@
@EnforcePermission("USE_BIOMETRIC_INTERNAL")
void onUdfpsUiEvent(int event, long requestId, int sensorId);
+ @EnforcePermission("USE_BIOMETRIC_INTERNAL")
+ void setIgnoreDisplayTouches(long requestId, int sensorId, boolean ignoreTouches);
+
// Sets the controller for managing the UDFPS overlay.
@EnforcePermission("USE_BIOMETRIC_INTERNAL")
void setUdfpsOverlayController(in IUdfpsOverlayController controller);
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 6458534..f026997 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -216,3 +216,11 @@
description: "Allow privileged apps to call bugreport generation without enforcing user consent and delegate it to the calling app instead"
bug: "324046728"
}
+
+flag {
+ name: "get_private_space_settings"
+ namespace: "profile_experiences"
+ description: "Guards a new Private Profile API in LauncherApps"
+ bug: "346294653"
+ is_exported: true
+}
diff --git a/core/java/android/os/vibrator/VibrationConfig.java b/core/java/android/os/vibrator/VibrationConfig.java
index 555a120..9c2fb7b 100644
--- a/core/java/android/os/vibrator/VibrationConfig.java
+++ b/core/java/android/os/vibrator/VibrationConfig.java
@@ -70,7 +70,7 @@
private final boolean mDefaultKeyboardVibrationEnabled;
- private final boolean mHasFixedKeyboardAmplitude;
+ private final boolean mKeyboardVibrationSettingsSupported;
/** @hide */
public VibrationConfig(@Nullable Resources resources) {
@@ -89,8 +89,8 @@
com.android.internal.R.bool.config_ignoreVibrationsOnWirelessCharger, false);
mDefaultKeyboardVibrationEnabled = loadBoolean(resources,
com.android.internal.R.bool.config_defaultKeyboardVibrationEnabled, true);
- mHasFixedKeyboardAmplitude = loadFloat(resources,
- com.android.internal.R.dimen.config_keyboardHapticFeedbackFixedAmplitude, -1) > 0;
+ mKeyboardVibrationSettingsSupported = loadBoolean(resources,
+ com.android.internal.R.bool.config_keyboardVibrationSettingsSupported, false);
mDefaultAlarmVibrationIntensity = loadDefaultIntensity(resources,
com.android.internal.R.integer.config_defaultAlarmVibrationIntensity);
@@ -202,11 +202,11 @@
}
/**
- * Whether the device has a fixed amplitude for keyboard.
+ * Whether the device support keyboard vibration settings.
* @hide
*/
- public boolean hasFixedKeyboardAmplitude() {
- return mHasFixedKeyboardAmplitude;
+ public boolean isKeyboardVibrationSettingsSupported() {
+ return mKeyboardVibrationSettingsSupported;
}
/** Get the default vibration intensity for given usage. */
@@ -249,6 +249,7 @@
+ ", mDefaultNotificationIntensity=" + mDefaultNotificationVibrationIntensity
+ ", mDefaultRingIntensity=" + mDefaultRingVibrationIntensity
+ ", mDefaultKeyboardVibrationEnabled=" + mDefaultKeyboardVibrationEnabled
+ + ", mKeyboardVibrationSettingsSupported=" + mKeyboardVibrationSettingsSupported
+ "}";
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 94c76929..c954cdb 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6178,6 +6178,15 @@
public static final String POINTER_FILL_STYLE = "pointer_fill_style";
/**
+ * Pointer stroke style, specified by
+ * {@link android.view.PointerIcon.PointerIconVectorStyleStroke} constants.
+ *
+ * @hide
+ */
+ @Readable
+ public static final String POINTER_STROKE_STYLE = "pointer_stroke_style";
+
+ /**
* Whether lock-to-app will be triggered by long-press on recents.
* @hide
*/
@@ -6380,6 +6389,7 @@
PRIVATE_SETTINGS.add(SIP_ASK_ME_EACH_TIME);
PRIVATE_SETTINGS.add(POINTER_SPEED);
PRIVATE_SETTINGS.add(POINTER_FILL_STYLE);
+ PRIVATE_SETTINGS.add(POINTER_STROKE_STYLE);
PRIVATE_SETTINGS.add(POINTER_SCALE);
PRIVATE_SETTINGS.add(LOCK_TO_APP_ENABLED);
PRIVATE_SETTINGS.add(EGG_MODE);
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 863a99a..e16a6a1 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -2941,7 +2941,7 @@
long latestEndTime = -1;
// DND turned on by manual rule
- if (config.manualRule != null) {
+ if (config.isManualActive()) {
final Uri id = config.manualRule.conditionId;
if (config.manualRule.enabler != null) {
// app triggered manual rule
@@ -2950,7 +2950,7 @@
secondaryText = appName;
}
} else {
- if (id == null) {
+ if (id == null || Uri.EMPTY.equals(id)) {
// Do not disturb manually triggered to remain on forever until turned off
if (describeForeverCondition) {
return context.getString(R.string.zen_mode_forever);
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index c302126..1535145 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -193,6 +193,25 @@
/** @hide */ public static final int POINTER_ICON_VECTOR_STYLE_FILL_END =
POINTER_ICON_VECTOR_STYLE_FILL_BLUE;
+ /** @hide */
+ @IntDef(prefix = {"POINTER_ICON_VECTOR_STYLE_STROKE_"}, value = {
+ POINTER_ICON_VECTOR_STYLE_STROKE_WHITE,
+ POINTER_ICON_VECTOR_STYLE_STROKE_BLACK,
+ POINTER_ICON_VECTOR_STYLE_STROKE_NONE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PointerIconVectorStyleStroke {}
+
+ /** @hide */ public static final int POINTER_ICON_VECTOR_STYLE_STROKE_WHITE = 0;
+ /** @hide */ public static final int POINTER_ICON_VECTOR_STYLE_STROKE_BLACK = 1;
+ /** @hide */ public static final int POINTER_ICON_VECTOR_STYLE_STROKE_NONE = 2;
+
+ // If adding PointerIconVectorStyleStroke, update END value for {@link SystemSettingsValidators}
+ /** @hide */ public static final int POINTER_ICON_VECTOR_STYLE_STROKE_BEGIN =
+ POINTER_ICON_VECTOR_STYLE_STROKE_WHITE;
+ /** @hide */ public static final int POINTER_ICON_VECTOR_STYLE_STROKE_END =
+ POINTER_ICON_VECTOR_STYLE_STROKE_NONE;
+
/** @hide */ public static final float DEFAULT_POINTER_SCALE = 1f;
/** @hide */ public static final float LARGE_POINTER_SCALE = 2.5f;
@@ -712,6 +731,23 @@
}
/**
+ * Convert stroke style constant to resource ID.
+ *
+ * @hide
+ */
+ public static int vectorStrokeStyleToResource(@PointerIconVectorStyleStroke int strokeStyle) {
+ return switch (strokeStyle) {
+ case POINTER_ICON_VECTOR_STYLE_STROKE_BLACK ->
+ com.android.internal.R.style.PointerIconVectorStyleStrokeBlack;
+ case POINTER_ICON_VECTOR_STYLE_STROKE_WHITE ->
+ com.android.internal.R.style.PointerIconVectorStyleStrokeWhite;
+ case POINTER_ICON_VECTOR_STYLE_STROKE_NONE ->
+ com.android.internal.R.style.PointerIconVectorStyleStrokeNone;
+ default -> com.android.internal.R.style.PointerIconVectorStyleStrokeWhite;
+ };
+ }
+
+ /**
* Sets whether drop shadow will draw in the native code.
*
* @hide
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 766e02b..4766942 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -13859,11 +13859,6 @@
})
@ResolvedLayoutDir
public int getLayoutDirection() {
- final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
- if (targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR1) {
- mPrivateFlags2 |= PFLAG2_LAYOUT_DIRECTION_RESOLVED;
- return LAYOUT_DIRECTION_RESOLVED_DEFAULT;
- }
return ((mPrivateFlags2 & PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL) ==
PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL) ? LAYOUT_DIRECTION_RTL : LAYOUT_DIRECTION_LTR;
}
diff --git a/core/java/android/view/ViewTraversalTracingStrings.java b/core/java/android/view/ViewTraversalTracingStrings.java
index 7dde87b..e95205b 100644
--- a/core/java/android/view/ViewTraversalTracingStrings.java
+++ b/core/java/android/view/ViewTraversalTracingStrings.java
@@ -58,6 +58,6 @@
out.append(" ");
out.append(className);
v.appendId(out);
- return out.substring(0, Math.min(out.length() - 1, Trace.MAX_SECTION_NAME_LEN - 1));
+ return out.substring(0, Math.min(out.length(), Trace.MAX_SECTION_NAME_LEN));
}
}
diff --git a/core/java/android/window/TaskFragmentAnimationParams.java b/core/java/android/window/TaskFragmentAnimationParams.java
index 85e96c9..67b22f9 100644
--- a/core/java/android/window/TaskFragmentAnimationParams.java
+++ b/core/java/android/window/TaskFragmentAnimationParams.java
@@ -171,7 +171,7 @@
*/
public boolean hasOverrideAnimation() {
return mOpenAnimationResId != DEFAULT_ANIMATION_RESOURCES_ID
- || mChangeAnimationResId != DEFAULT_ANIMATION_BACKGROUND_COLOR
+ || mChangeAnimationResId != DEFAULT_ANIMATION_RESOURCES_ID
|| mCloseAnimationResId != DEFAULT_ANIMATION_RESOURCES_ID;
}
diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
index 5b99ff9..cc880e1 100644
--- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
+++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
@@ -113,4 +113,12 @@
namespace: "large_screen_experiences_app_compat"
description: "Enables sysui animation for user aspect ratio button"
bug: "300357441"
+}
+
+flag {
+ name: "app_compat_ui_framework"
+ namespace: "large_screen_experiences_app_compat"
+ description: "Whether the declarative compat UI framework is enabled"
+ bug: "270361630"
+ is_fixed_read_only: true
}
\ No newline at end of file
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index d0ab674..1c7acd4 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -127,3 +127,10 @@
description: "Whether to show developer option for enabling desktop windowing mode"
bug: "348193756"
}
+
+flag {
+ name: "enable_desktop_windowing_app_to_web"
+ namespace: "lse_desktop_experience"
+ description: "Whether to enable the app-to-web feature and show the open in browser button in the header menu"
+ bug: "349695493"
+}
diff --git a/core/proto/android/providers/settings/system.proto b/core/proto/android/providers/settings/system.proto
index 6a0ec1d..e5ced25 100644
--- a/core/proto/android/providers/settings/system.proto
+++ b/core/proto/android/providers/settings/system.proto
@@ -126,6 +126,7 @@
option (android.msg_privacy).dest = DEST_EXPLICIT;
optional SettingProto pointer_fill_style = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto pointer_stroke_style = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto pointer_scale = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional Pointer pointer = 37;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index f61b6bf..9f00d5e 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -843,6 +843,7 @@
<protected-broadcast android:name="android.intent.action.PROFILE_UNAVAILABLE" />
<protected-broadcast android:name="android.app.action.CONSOLIDATED_NOTIFICATION_POLICY_CHANGED" />
<protected-broadcast android:name="android.intent.action.MAIN_USER_LOCKSCREEN_KNOWLEDGE_FACTOR_CHANGED" />
+ <protected-broadcast android:name="com.android.uwb.uwbcountrycode.GEOCODE_RETRY" />
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
diff --git a/core/res/res/drawable/pointer_alias_vector.xml b/core/res/res/drawable/pointer_alias_vector.xml
index 035a099..60149f1 100644
--- a/core/res/res/drawable/pointer_alias_vector.xml
+++ b/core/res/res/drawable/pointer_alias_vector.xml
@@ -22,10 +22,10 @@
android:fillColor="#00FFFFFF"
android:pathData="M14.494 12.779a5.2 5.2 0 0 1-1.771 1.414 5.2 5.2 0 0 1-1.68.489l.81 1.658a1.968 1.968 0 0 0 3.536-1.728zM12.03 8.291l-.81-1.658a1.968 1.968 0 0 0-3.536 1.728l.896 1.833a5.2 5.2 0 0 1 1.77-1.414 5.2 5.2 0 0 1 1.68-.489" />
<path
- android:fillColor="#FFFFFF"
+ android:fillColor="?attr/pointerIconVectorStroke"
android:pathData="m18.323 13.178-.975-1.995a5.2 5.2 0 0 0-1.704-1.978 5.2 5.2 0 0 0-.517-2.01L14.152 5.2a5.232 5.232 0 0 0-9.401 4.594l.975 1.995a5.2 5.2 0 0 0 1.704 1.978 5.2 5.2 0 0 0 .517 2.01l.975 1.995a5.233 5.233 0 0 0 9.401-4.594m-2.843 6.1a4.23 4.23 0 0 1-5.66-1.944l-.975-1.995a4.2 4.2 0 0 1-.431-1.838l-.001-.276-.234-.146a4.2 4.2 0 0 1-1.555-1.729L5.65 9.355a4.232 4.232 0 1 1 7.604-3.716l.975 1.995c.29.594.428 1.22.431 1.838l.001.276.234.146c.648.405 1.194.99 1.555 1.728l.975 1.995a4.234 4.234 0 0 1-1.945 5.661" />
<path
- android:fillColor="#FFFFFF"
+ android:fillColor="?attr/pointerIconVectorStroke"
android:pathData="M15.313 12.177a3 3 0 0 0-.416-.633l-.459-.534-.353.609a4.2 4.2 0 0 1-1.801 1.675 4.2 4.2 0 0 1-1.977.429l-.704-.02.213.671q.066.208.164.409l.975 1.995a2.967 2.967 0 1 0 5.332-2.606zm-.827 5.066a1.97 1.97 0 0 1-2.632-.904l-.81-1.658a5.2 5.2 0 0 0 1.68-.489 5.2 5.2 0 0 0 1.771-1.414l.896 1.833a1.97 1.97 0 0 1-.905 2.632m-3.697-7.565a4.2 4.2 0 0 1 1.977-.429l.704.02-.213-.671a3 3 0 0 0-.164-.409l-.975-1.995A2.967 2.967 0 1 0 6.785 8.8l.975 1.995q.172.35.416.633l.459.534.353-.609a4.2 4.2 0 0 1 1.801-1.675m-2.21.516-.895-1.833a1.968 1.968 0 1 1 3.536-1.728l.81 1.658a5.2 5.2 0 0 0-1.68.489 5.2 5.2 0 0 0-1.771 1.414m3.151 1.965a3 3 0 0 0 1.02-.818l.755-.95-1.205.142a2.97 2.97 0 0 0-1.975 1.1l-.755.95 1.205-.142c.324-.039.646-.132.955-.282" />
<path
android:fillColor="?attr/pointerIconVectorFill"
diff --git a/core/res/res/drawable/pointer_all_scroll_vector.xml b/core/res/res/drawable/pointer_all_scroll_vector.xml
index 45ad98c..41314ed 100644
--- a/core/res/res/drawable/pointer_all_scroll_vector.xml
+++ b/core/res/res/drawable/pointer_all_scroll_vector.xml
@@ -24,5 +24,5 @@
android:fillColor="?attr/pointerIconVectorFill"/>
<path
android:pathData="M12 4c.36 0 .72.18.93.54l1.75 3.06c.41.71-.1 1.6-.92 1.6h-.82v1.86h1.86v-.84a1.07 1.07 0 0 1 1.6-.92l3.06 1.75c.72.41.72 1.44 0 1.85l-3.06 1.76a1.07 1.07 0 0 1-1.6-.92v-.8h-1.86v1.87h.82c.82 0 1.33.88.92 1.6l-1.75 3.06a1.07 1.07 0 0 1-1.85 0L9.32 16.4c-.4-.7.1-1.6.93-1.6h.81v-1.86H9.2v.8a1.07 1.07 0 0 1-1.6.92L4.54 12.9a1.06 1.06 0 0 1 0-1.85L7.6 9.3a1.07 1.07 0 0 1 1.6.92v.85h1.86V9.2h-.82c-.81 0-1.33-.89-.92-1.6l1.76-3.06c.2-.36.56-.54.92-.54m0-1c-.74 0-1.41.39-1.79 1.04L8.45 7.1c-.18.33-.28.7-.27 1.05h-.05c-.36 0-.71.1-1.03.28l-3.06 1.76a2.05 2.05 0 0 0 0 3.58l3.06 1.75c.32.18.67.28 1.03.28h.05c-.01.38.08.76.28 1.1l1.75 3.07c.38.65 1.05 1.03 1.8 1.03s1.41-.38 1.78-1.03l1.76-3.07c.2-.34.3-.72.28-1.1h.04c.36 0 .71-.1 1.03-.28l3.06-1.75a2.07 2.07 0 0 0 0-3.58L16.9 8.43a2.07 2.07 0 0 0-1.03-.28h-.04c0-.36-.09-.72-.28-1.05L13.8 4.04A2.04 2.04 0 0 0 12 3z"
- android:fillColor="#FFFFFF"/>
+ android:fillColor="?attr/pointerIconVectorStroke"/>
</vector>
diff --git a/core/res/res/drawable/pointer_arrow_vector.xml b/core/res/res/drawable/pointer_arrow_vector.xml
index 2614170..a0bd5b6 100644
--- a/core/res/res/drawable/pointer_arrow_vector.xml
+++ b/core/res/res/drawable/pointer_arrow_vector.xml
@@ -24,5 +24,5 @@
android:fillColor="?attr/pointerIconVectorFill"/>
<path
android:pathData="M16.94 10.38 7.37 3.22a2.77 2.77 0 0 0-2.93-.27 2.75 2.75 0 0 0-1.55 2.51l.01 11.95a2.78 2.78 0 0 0 2.82 2.8c.77 0 1.5-.32 2.03-.9l2.97-3.19a.8.8 0 0 1 .5-.25l4.34-.46a2.76 2.76 0 0 0 2.4-2.05 2.8 2.8 0 0 0-1.02-2.98zM17 13.1a1.77 1.77 0 0 1-1.55 1.31l-4.33.47a1.8 1.8 0 0 0-1.13.56l-2.97 3.2c-.4.42-.86.57-1.3.57-.24 0-.48-.05-.68-.13a1.77 1.77 0 0 1-1.14-1.67V5.46a1.81 1.81 0 0 1 1.8-1.8c.38 0 .75.11 1.07.36l9.57 7.16c.72.54.81 1.35.66 1.92z"
- android:fillColor="#FFFFFF"/>
+ android:fillColor="?attr/pointerIconVectorStroke"/>
</vector>
diff --git a/core/res/res/drawable/pointer_cell_vector.xml b/core/res/res/drawable/pointer_cell_vector.xml
index cead1c4..9ad64e27 100644
--- a/core/res/res/drawable/pointer_cell_vector.xml
+++ b/core/res/res/drawable/pointer_cell_vector.xml
@@ -19,7 +19,7 @@
android:viewportHeight="24"
android:viewportWidth="24">
<path
- android:fillColor="#FFFFFF"
+ android:fillColor="?attr/pointerIconVectorStroke"
android:pathData="M19 9.667h-4.668V5a2 2 0 0 0-2-2h-.667a2 2 0 0 0-2 2v4.667H5a2 2 0 0 0-2 2v.667a2 2 0 0 0 2 2h4.665V19a2 2 0 0 0 2 2h.667a2 2 0 0 0 2-2v-4.666H19a2 2 0 0 0 2-2v-.667a2 2 0 0 0-2-2m1 2.667a1 1 0 0 1-1 1h-5.668V19a1 1 0 0 1-1 1h-.667a1 1 0 0 1-1-1v-5.666H5a1 1 0 0 1-1-1v-.667a1 1 0 0 1 1-1h5.665V5a1 1 0 0 1 1-1h.667a1 1 0 0 1 1 1v5.667H19a1 1 0 0 1 1 1z" />
<path
android:fillColor="?attr/pointerIconVectorFill"
diff --git a/core/res/res/drawable/pointer_context_menu_vector.xml b/core/res/res/drawable/pointer_context_menu_vector.xml
index fb2af43..33b1ace 100644
--- a/core/res/res/drawable/pointer_context_menu_vector.xml
+++ b/core/res/res/drawable/pointer_context_menu_vector.xml
@@ -19,11 +19,11 @@
android:viewportHeight="24"
android:viewportWidth="24">
<group>
- <path android:fillColor="#FFFFFF" android:pathData="M19.475 2.604h-2.66c-.842 0-1.527.685-1.527 1.527v2.66c0 .842.685 1.527 1.527 1.527h2.66c.842 0 1.527-.685 1.527-1.527v-2.66c0-.842-.685-1.527-1.527-1.527m.67 4.187c0 .37-.3.67-.67.67h-2.66a.67.67 0 0 1-.67-.67v-2.66c0-.37.3-.67.67-.67h2.66c.37 0 .67.3.67.67z" />
- <path android:fillColor="#FFFFFF" android:pathData="M19.175 4.17h-2.067a.3.3 0 1 0 0 .6h2.067a.3.3 0 1 0 0-.6m0 .886h-2.067a.3.3 0 1 0 0 .6h2.067a.3.3 0 1 0 0-.6m0 .868h-2.067a.3.3 0 1 0 0 .6h2.067a.3.3 0 1 0 0-.6" />
+ <path android:fillColor="?attr/pointerIconVectorStroke" android:pathData="M19.475 2.604h-2.66c-.842 0-1.527.685-1.527 1.527v2.66c0 .842.685 1.527 1.527 1.527h2.66c.842 0 1.527-.685 1.527-1.527v-2.66c0-.842-.685-1.527-1.527-1.527m.67 4.187c0 .37-.3.67-.67.67h-2.66a.67.67 0 0 1-.67-.67v-2.66c0-.37.3-.67.67-.67h2.66c.37 0 .67.3.67.67z" />
+ <path android:fillColor="?attr/pointerIconVectorStroke" android:pathData="M19.175 4.17h-2.067a.3.3 0 1 0 0 .6h2.067a.3.3 0 1 0 0-.6m0 .886h-2.067a.3.3 0 1 0 0 .6h2.067a.3.3 0 1 0 0-.6m0 .868h-2.067a.3.3 0 1 0 0 .6h2.067a.3.3 0 1 0 0-.6" />
</group>
<path
- android:fillColor="#FFFFFF"
+ android:fillColor="?attr/pointerIconVectorStroke"
android:pathData="M16.938 10.38 7.372 3.216a2.77 2.77 0 0 0-2.931-.262A2.75 2.75 0 0 0 2.894 5.46l.009 11.951a2.785 2.785 0 0 0 1.776 2.604c.33.129.691.197 1.044.197a2.75 2.75 0 0 0 2.031-.897l2.969-3.193a.8.8 0 0 1 .5-.25l4.336-.467c1.397-.15 2.157-1.153 2.401-2.041a2.785 2.785 0 0 0-1.022-2.984m.058 2.718c-.157.571-.645 1.216-1.544 1.312l-4.335.467a1.8 1.8 0 0 0-1.126.563l-2.97 3.193a1.74 1.74 0 0 1-1.298.578 1.9 1.9 0 0 1-.678-.128c-.551-.217-1.141-.771-1.142-1.674l-.009-11.95c0-.697.371-1.299.994-1.611.262-.131.538-.196.813-.196.377 0 .75.123 1.072.365l9.566 7.163c.723.542.814 1.346.657 1.918" />
<path
android:fillColor="?attr/pointerIconVectorFill"
diff --git a/core/res/res/drawable/pointer_copy_vector.xml b/core/res/res/drawable/pointer_copy_vector.xml
index 3f13868..8ac8c21 100644
--- a/core/res/res/drawable/pointer_copy_vector.xml
+++ b/core/res/res/drawable/pointer_copy_vector.xml
@@ -23,7 +23,7 @@
<path android:fillColor="?attr/pointerIconVectorFillInverse" android:pathData="M19.299 6H18V4.7a.5.5 0 0 0-1 0V6h-1.301a.5.5 0 0 0 0 1H17v1.3a.5.5 0 0 0 1 0V7h1.299a.5.5 0 0 0 0-1" />
</group>
<path
- android:fillColor="#000000"
+ android:fillColor="?attr/pointerIconVectorStrokeInverse"
android:pathData="M18.485 10.884v1.739q.013.189.013.38c0 3.045-2.518 5.514-5.563 5.514a5.58 5.58 0 0 1-3.922-1.613 1 1 0 0 1-.117-.106l-3.847-3.936c-.482-.494-.482-1.294 0-1.787s1.265-.494 1.747 0l.697.713V7.583a1 1 0 0 1 2 0v1.096q.463-.364.997-.625v-1.57a1 1 0 0 1 2 0v1.022q.22-.018.446-.018c.062 0 .123.007.185.009A4.4 4.4 0 0 1 13 6.5c0-.378.061-.739.149-1.09a1.97 1.97 0 0 0-1.659-.926c-.903 0-1.658.603-1.906 1.425a1.997 1.997 0 0 0-3.091 1.674v2.206a2.2 2.2 0 0 0-2.159.586 2.285 2.285 0 0 0 0 3.185l3.847 3.936q.063.061.13.118l-.001.001a6.58 6.58 0 0 0 4.624 1.902c3.586 0 6.563-2.905 6.563-6.514q-.001-.192-.013-.381v-2.108c-.316.156-.645.29-.999.37" />
<path
android:fillColor="#1FA54A"
diff --git a/core/res/res/drawable/pointer_crosshair_vector.xml b/core/res/res/drawable/pointer_crosshair_vector.xml
index 8a50d1b..4fba080 100644
--- a/core/res/res/drawable/pointer_crosshair_vector.xml
+++ b/core/res/res/drawable/pointer_crosshair_vector.xml
@@ -19,7 +19,7 @@
android:viewportHeight="24"
android:viewportWidth="24">
<path
- android:fillColor="#FFFFFF"
+ android:fillColor="?attr/pointerIconVectorStroke"
android:pathData="M19.25 10.25h-5.5v-5.5a1.75 1.75 0 0 0-3.5 0v5.5h-5.5a1.75 1.75 0 0 0 0 3.5h5.5v5.5a1.75 1.75 0 0 0 3.5 0v-5.5h5.5a1.75 1.75 0 0 0 0-3.5m0 2.5h-6.5v6.5a.75.75 0 0 1-1.5 0v-6.5h-6.5a.75.75 0 0 1 0-1.5h6.5v-6.5a.75.75 0 0 1 1.5 0v6.5h6.5a.75.75 0 0 1 0 1.5" />
<path
android:fillType="evenOdd"
diff --git a/core/res/res/drawable/pointer_grab_vector.xml b/core/res/res/drawable/pointer_grab_vector.xml
index 48c01ce..d9e1f18 100644
--- a/core/res/res/drawable/pointer_grab_vector.xml
+++ b/core/res/res/drawable/pointer_grab_vector.xml
@@ -19,7 +19,7 @@
android:viewportHeight="24"
android:viewportWidth="24">
<path
- android:fillColor="#000000"
+ android:fillColor="?attr/pointerIconVectorStrokeInverse"
android:pathData="M20.442 7.562a2 2 0 0 0-2-2c-.366 0-.705.106-1 .277V4.686a2 2 0 0 0-2-2 2 2 0 0 0-1.004.279 1.995 1.995 0 0 0-3.986-.06 2 2 0 0 0-1.006-.28 2 2 0 0 0-2 2v6.501l-.247-.253a2.216 2.216 0 0 0-3.178 0 2.286 2.286 0 0 0 0 3.186l5.106 5.224q.063.061.131.118l-.001.001a6.58 6.58 0 0 0 4.624 1.901c3.587 0 6.565-2.906 6.565-6.516q0-.105-.004-.21m-6.561 5.727a5.58 5.58 0 0 1-3.922-1.613 1 1 0 0 1-.117-.106l-5.106-5.224a1.286 1.286 0 0 1 0-1.788 1.215 1.215 0 0 1 1.747 0l1.962 2.008V4.625a1 1 0 0 1 2 0v5.833q.463-.362.996-.623V3a1 1 0 0 1 2 0v6.29a6 6 0 0 1 1 .011V4.686a1 1 0 0 1 2 0v5.21c.357.185.693.408 1 .663V7.562a1 1 0 0 1 2 0v7.019h.001-.001q.004.104.004.207c.001 3.046-2.518 5.516-5.564 5.516" />
<path
android:fillColor="?attr/pointerIconVectorFillInverse"
diff --git a/core/res/res/drawable/pointer_grabbing_vector.xml b/core/res/res/drawable/pointer_grabbing_vector.xml
index ad9f86c..7f52fc5 100644
--- a/core/res/res/drawable/pointer_grabbing_vector.xml
+++ b/core/res/res/drawable/pointer_grabbing_vector.xml
@@ -19,7 +19,7 @@
android:viewportHeight="24"
android:viewportWidth="24">
<path
- android:fillColor="#000000"
+ android:fillColor="?attr/pointerIconVectorStrokeInverse"
android:pathData="M19.485 12.622V8.508a2 2 0 0 0-3.12-1.657 1.993 1.993 0 0 0-2.99-1.006 1.99 1.99 0 0 0-1.886-1.361c-.903 0-1.658.603-1.906 1.425a2 2 0 0 0-3.09 1.674v2.206a2.2 2.2 0 0 0-2.159.586 2.285 2.285 0 0 0 0 3.185l3.847 3.936q.063.061.13.118l-.001.001a6.58 6.58 0 0 0 4.624 1.902c3.586 0 6.563-2.905 6.563-6.514a5 5 0 0 0-.012-.381m-6.55 5.895a5.58 5.58 0 0 1-3.922-1.613 1 1 0 0 1-.117-.106l-3.847-3.936c-.482-.494-.482-1.294 0-1.787s1.265-.494 1.747 0l.697.713V7.583a1 1 0 0 1 2 0v1.096q.463-.364.997-.625v-1.57a1 1 0 0 1 2 0v1.022a5.5 5.5 0 0 1 .996.009v-.007a1 1 0 0 1 2 0v.599q.537.277 1 .66v-.259a1 1 0 0 1 2 0v4.115q.013.189.013.38c-.001 3.045-2.518 5.514-5.564 5.514" />
<path
android:fillColor="?attr/pointerIconVectorFillInverse"
diff --git a/core/res/res/drawable/pointer_hand_vector.xml b/core/res/res/drawable/pointer_hand_vector.xml
index a06dc08..115b53b 100644
--- a/core/res/res/drawable/pointer_hand_vector.xml
+++ b/core/res/res/drawable/pointer_hand_vector.xml
@@ -19,7 +19,7 @@
android:viewportHeight="24"
android:viewportWidth="24">
<path
- android:fillColor="#000000"
+ android:fillColor="?attr/pointerIconVectorStrokeInverse"
android:pathData="M20.492 15.197v-4.198A1.995 1.995 0 0 0 18.5 9.001c-.413 0-.797.126-1.115.342a1.99 1.99 0 0 0-1.873-1.341c-.411 0-.792.125-1.109.339a1.99 1.99 0 0 0-1.879-1.361c-.363 0-.699.105-.992.275V3.998A1.99 1.99 0 0 0 9.542 2c-1.1 0-1.992.895-1.992 1.998v7.831l-.242-.249a2.2 2.2 0 0 0-3.164 0 2.29 2.29 0 0 0 0 3.183l5.084 5.219q.063.061.13.118l-.001.001A6.54 6.54 0 0 0 13.963 22c3.572 0 6.537-2.903 6.537-6.509q0-.148-.008-.294m-6.529 5.804a5.55 5.55 0 0 1-3.906-1.611 1 1 0 0 1-.117-.106l-5.084-5.219a1.286 1.286 0 0 1 0-1.786 1.21 1.21 0 0 1 1.74 0l1.95 2.002V3.998c0-.552.446-.999.996-.999s.996.447.996.999v7.17l.011-.007a.495.495 0 0 0 .989-.037V8.939a.992.992 0 0 1 1.984.039v.796l-.007 1.386a.5.5 0 0 0 .495.502h.003a.5.5 0 0 0 .498-.497l.006-1.157h.001V10a.997.997 0 1 1 1.991 0v.601l.004.003v1.02q.001.107.042.199a.5.5 0 0 0 .153.187l.031.021a.5.5 0 0 0 .231.083c.014.001.026.008.04.008a.5.5 0 0 0 .498-.5v-.642a.996.996 0 0 1 .993-.98c.55 0 .996.447.996.999v4.199a6 6 0 0 1 .008.293c-.001 3.043-2.509 5.51-5.542 5.51" />
<path
android:fillColor="?attr/pointerIconVectorFillInverse"
diff --git a/core/res/res/drawable/pointer_handwriting_vector.xml b/core/res/res/drawable/pointer_handwriting_vector.xml
index 8497592..6f62aba 100644
--- a/core/res/res/drawable/pointer_handwriting_vector.xml
+++ b/core/res/res/drawable/pointer_handwriting_vector.xml
@@ -19,8 +19,8 @@
android:viewportHeight="24"
android:viewportWidth="24">
<group>
- <path android:fillColor="#FFFFFF" android:pathData="M20.12 6.8 18.7 5.38c-.57-.57-1.32-.88-2.12-.88s-1.56.31-2.13.88l-7.16 7.16-.29.29V5c0-1.1-.9-2-2-2s-2 .9-2 2v14c0 1.1.9 2 2 2s2-.9 2-2v-.5h5.67l.29-.29 7.16-7.16c.57-.57.88-1.32.88-2.12s-.31-1.57-.88-2.13M6 19c0 .55-.45 1-1 1s-1-.45-1-1V5c0-.55.45-1 1-1s1 .45 1 1zm13.41-8.66-7.16 7.16H8v-4.25l7.16-7.16c.39-.39.9-.59 1.41-.59h.01c.51 0 1.02.2 1.41.59l1.42 1.42c.78.78.78 2.05 0 2.83" />
- <path android:fillColor="#FFFFFF" android:pathData="m16.431 7.64-6.29 6.29 1.43 1.43 6.29-6.29-1.42-1.43z" />
+ <path android:fillColor="?attr/pointerIconVectorStroke" android:pathData="M20.12 6.8 18.7 5.38c-.57-.57-1.32-.88-2.12-.88s-1.56.31-2.13.88l-7.16 7.16-.29.29V5c0-1.1-.9-2-2-2s-2 .9-2 2v14c0 1.1.9 2 2 2s2-.9 2-2v-.5h5.67l.29-.29 7.16-7.16c.57-.57.88-1.32.88-2.12s-.31-1.57-.88-2.13M6 19c0 .55-.45 1-1 1s-1-.45-1-1V5c0-.55.45-1 1-1s1 .45 1 1zm13.41-8.66-7.16 7.16H8v-4.25l7.16-7.16c.39-.39.9-.59 1.41-.59h.01c.51 0 1.02.2 1.41.59l1.42 1.42c.78.78.78 2.05 0 2.83" />
+ <path android:fillColor="?attr/pointerIconVectorStroke" android:pathData="m16.431 7.64-6.29 6.29 1.43 1.43 6.29-6.29-1.42-1.43z" />
</group>
<path
android:fillColor="?attr/pointerIconVectorFill"
diff --git a/core/res/res/drawable/pointer_help_vector.xml b/core/res/res/drawable/pointer_help_vector.xml
index 07970fb..63cf287 100644
--- a/core/res/res/drawable/pointer_help_vector.xml
+++ b/core/res/res/drawable/pointer_help_vector.xml
@@ -22,7 +22,7 @@
android:fillColor="?attr/pointerIconVectorFill"
android:pathData="M16.339 11.18 6.773 4.017a1.78 1.78 0 0 0-1.072-.365c-.274 0-.551.065-.813.196a1.77 1.77 0 0 0-.994 1.611l.009 11.951c0 .903.59 1.457 1.142 1.674.2.078.433.128.678.128.434 0 .906-.155 1.298-.578l2.97-3.193a1.8 1.8 0 0 1 1.126-.563l4.335-.467c.899-.097 1.387-.741 1.544-1.312.157-.573.066-1.377-.657-1.919" />
<path
- android:fillColor="#FFFFFF"
+ android:fillColor="?attr/pointerIconVectorStroke"
android:pathData="M16.94 10.38 7.37 3.22a2.77 2.77 0 0 0-2.93-.27A2.75 2.75 0 0 0 2.9 5.46l.01 11.95a2.79 2.79 0 0 0 2.82 2.8c.78 0 1.5-.32 2.03-.9l2.97-3.19a.8.8 0 0 1 .5-.25l4.34-.46a2.76 2.76 0 0 0 2.4-2.05 2.8 2.8 0 0 0-1.02-2.98zM17 13.1a1.77 1.77 0 0 1-1.55 1.31l-4.33.47a1.8 1.8 0 0 0-1.13.56l-2.97 3.2c-.4.42-.86.57-1.3.57-.24 0-.48-.05-.68-.13a1.77 1.77 0 0 1-1.14-1.67V5.46a1.81 1.81 0 0 1 1.8-1.8c.38 0 .75.11 1.07.36l9.57 7.16c.72.54.81 1.35.66 1.92zm2.64-10.83a2.5 2.5 0 0 0-1.84-.72 3 3 0 0 0-2.83 1.93l-.39.94.96.37.86.32.12.05-.02.03c-.22.4-.3.82-.3 1.33v.94a1.56 1.56 0 0 0 .4 1.47 1.54 1.54 0 0 0 2.24.01 1.55 1.55 0 0 0 .28-1.84v-.52c0-.1.02-.17.03-.25l.16-.15c.32-.25.6-.56.78-.93.18-.37.26-.76.26-1.16 0-.68-.21-1.32-.7-1.82zm-1.5 5.96a.55.55 0 0 1-.82 0 .56.56 0 0 1-.17-.4c0-.16.06-.3.17-.4a.55.55 0 0 1 .41-.18c.15 0 .28.06.4.17a.55.55 0 0 1 0 .81zm1.05-3.42c-.1.22-.28.42-.52.6-.26.22-.42.42-.47.6-.05.18-.08.37-.08.57l-.93-.06c0-.38.07-.62.19-.86.13-.24.3-.46.54-.66.17-.13.3-.28.4-.43s.14-.3.14-.46c0-.2-.08-.37-.22-.5s-.31-.17-.52-.17c-.2 0-.39.06-.56.18-.17.13-.3.31-.4.56l-.87-.33a2.03 2.03 0 0 1 1.91-1.3c.48 0 .86.14 1.13.42.28.28.41.65.41 1.12 0 .26-.05.5-.15.72z" />
<path
android:fillColor="?attr/pointerIconVectorFill"
diff --git a/core/res/res/drawable/pointer_horizontal_double_arrow_vector.xml b/core/res/res/drawable/pointer_horizontal_double_arrow_vector.xml
index 32c56b6..7aa6571 100644
--- a/core/res/res/drawable/pointer_horizontal_double_arrow_vector.xml
+++ b/core/res/res/drawable/pointer_horizontal_double_arrow_vector.xml
@@ -19,7 +19,7 @@
android:viewportHeight="24"
android:viewportWidth="24">
<path
- android:fillColor="#FFFFFF"
+ android:fillColor="?attr/pointerIconVectorStroke"
android:pathData="m19.963 10.185-3.065-1.758c-1.327-.761-2.96.14-3.072 1.633h-3.651c-.113-1.492-1.746-2.394-3.072-1.633l-3.065 1.758c-1.383.793-1.383 2.786 0 3.579l3.065 1.758c1.311.752 2.918-.12 3.065-1.581h3.666c.147 1.46 1.754 2.333 3.065 1.581l3.065-1.758c1.382-.793 1.382-2.786-.001-3.579m-.498 2.712L16.4 14.655a1.065 1.065 0 0 1-1.596-.922v-.791H9.195v.791c0 .818-.886 1.33-1.596.922l-3.065-1.758a1.063 1.063 0 0 1 0-1.845l3.065-1.758a1.065 1.065 0 0 1 1.596.922v.843h5.609v-.843c0-.818.886-1.33 1.596-.922l3.065 1.758a1.063 1.063 0 0 1 0 1.845" />
<path
android:fillColor="?attr/pointerIconVectorFill"
diff --git a/core/res/res/drawable/pointer_nodrop_vector.xml b/core/res/res/drawable/pointer_nodrop_vector.xml
index 6108e96..7cef01a 100644
--- a/core/res/res/drawable/pointer_nodrop_vector.xml
+++ b/core/res/res/drawable/pointer_nodrop_vector.xml
@@ -23,7 +23,7 @@
<path android:fillColor="?attr/pointerIconVectorFillInverse" android:pathData="M17.5 4c-.493 0-.95.148-1.337.395l3.442 3.442C19.852 7.45 20 6.993 20 6.5 20 5.121 18.879 4 17.5 4M15 6.5C15 7.879 16.121 9 17.5 9c.525 0 1.011-.164 1.413-.441l-3.472-3.472A2.5 2.5 0 0 0 15 6.5" />
</group>
<path
- android:fillColor="#000000"
+ android:fillColor="?attr/pointerIconVectorStrokeInverse"
android:pathData="M18.485 10.932v1.69q.013.188.013.38c0 3.045-2.518 5.514-5.563 5.514a5.58 5.58 0 0 1-3.922-1.613 1 1 0 0 1-.117-.106l-3.847-3.936c-.482-.494-.482-1.294 0-1.787s1.265-.494 1.747 0l.697.713V7.583a1 1 0 0 1 2 0v1.096q.463-.364.997-.625v-1.57a1 1 0 0 1 2 0v1.022q.22-.018.446-.018c.046 0 .09.006.135.007a4.5 4.5 0 0 1-.117-.995c0-.399.068-.779.165-1.148a1.97 1.97 0 0 0-1.629-.867c-.903 0-1.658.603-1.906 1.425a2 2 0 0 0-1.091-.327 2 2 0 0 0-2 2v2.206a2.2 2.2 0 0 0-2.159.586 2.285 2.285 0 0 0 0 3.185l3.847 3.936q.063.061.13.118l-.001.001a6.58 6.58 0 0 0 4.624 1.902c3.586 0 6.563-2.905 6.563-6.514q-.001-.192-.013-.381v-2.056a4.5 4.5 0 0 1-.999.366" />
<path
android:fillColor="#B22A25"
diff --git a/core/res/res/drawable/pointer_text_vector.xml b/core/res/res/drawable/pointer_text_vector.xml
index a147273..2fa1332 100644
--- a/core/res/res/drawable/pointer_text_vector.xml
+++ b/core/res/res/drawable/pointer_text_vector.xml
@@ -22,6 +22,6 @@
android:fillColor="?attr/pointerIconVectorFill"
android:pathData="M12 3c-.551 0-1 .448-1 1v14a1.001 1.001 0 0 0 2 0V4c0-.552-.449-1-1-1" />
<path
- android:fillColor="#FFFFFF"
+ android:fillColor="?attr/pointerIconVectorStroke"
android:pathData="M12 2c-1.103 0-2 .897-2 2v14c0 1.103.897 2 2 2s2-.897 2-2V4c0-1.103-.897-2-2-2m1 16a1.001 1.001 0 0 1-2 0V4a1.001 1.001 0 0 1 2 0z" />
</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_top_left_diagonal_double_arrow_vector.xml b/core/res/res/drawable/pointer_top_left_diagonal_double_arrow_vector.xml
index 7f95207..cd4e5e7 100644
--- a/core/res/res/drawable/pointer_top_left_diagonal_double_arrow_vector.xml
+++ b/core/res/res/drawable/pointer_top_left_diagonal_double_arrow_vector.xml
@@ -19,7 +19,7 @@
android:viewportHeight="24"
android:viewportWidth="24">
<path
- android:fillColor="#FFFFFF"
+ android:fillColor="?attr/pointerIconVectorStroke"
android:pathData="m18.896 16.365-.924-3.41c-.398-1.467-2.169-1.985-3.305-1.035L12.08 9.333c.952-1.136.434-2.908-1.034-3.306l-3.41-.924c-1.539-.416-2.948.993-2.532 2.532l.924 3.41c.398 1.468 2.17 1.986 3.306 1.034l2.586 2.586c-.953 1.136-.435 2.91 1.033 3.307l3.41.924c1.54.417 2.949-.992 2.533-2.531m-2.27 1.566-3.41-.924a1.065 1.065 0 0 1-.476-1.781l.579-.579-3.966-3.966-.579.579a1.066 1.066 0 0 1-1.781-.476L6.07 7.373a1.063 1.063 0 0 1 1.304-1.304l3.41.924a1.065 1.065 0 0 1 .476 1.781l-.578.578 3.966 3.966.577-.577a1.066 1.066 0 0 1 1.781.477l.924 3.41a1.062 1.062 0 0 1-1.304 1.303" />
<path
android:fillType="evenOdd"
diff --git a/core/res/res/drawable/pointer_top_right_diagonal_double_arrow_vector.xml b/core/res/res/drawable/pointer_top_right_diagonal_double_arrow_vector.xml
index 8a33715..3736290 100644
--- a/core/res/res/drawable/pointer_top_right_diagonal_double_arrow_vector.xml
+++ b/core/res/res/drawable/pointer_top_right_diagonal_double_arrow_vector.xml
@@ -19,7 +19,7 @@
android:viewportHeight="24"
android:viewportWidth="24">
<path
- android:fillColor="#FFFFFF"
+ android:fillColor="?attr/pointerIconVectorStroke"
android:pathData="m16.365 5.104-3.41.924c-1.468.398-1.986 2.171-1.033 3.307l-2.586 2.586c-1.136-.952-2.909-.434-3.306 1.034l-.924 3.41c-.417 1.539.992 2.948 2.531 2.531l3.41-.924c1.468-.398 1.986-2.17 1.034-3.306l2.587-2.587c1.136.951 2.908.432 3.305-1.035l.924-3.41c.415-1.538-.994-2.947-2.532-2.53m1.565 2.269-.924 3.41a1.065 1.065 0 0 1-1.781.476l-.577-.577-3.966 3.966.578.578a1.066 1.066 0 0 1-.476 1.781l-3.41.924a1.063 1.063 0 0 1-1.304-1.304l.924-3.41a1.066 1.066 0 0 1 1.781-.477l.578.578 3.966-3.966-.579-.579a1.066 1.066 0 0 1 .476-1.781l3.41-.924a1.063 1.063 0 0 1 1.304 1.305" />
<path
android:fillColor="?attr/pointerIconVectorFill"
diff --git a/core/res/res/drawable/pointer_vertical_double_arrow_vector.xml b/core/res/res/drawable/pointer_vertical_double_arrow_vector.xml
index 889372c..ff99f41 100644
--- a/core/res/res/drawable/pointer_vertical_double_arrow_vector.xml
+++ b/core/res/res/drawable/pointer_vertical_double_arrow_vector.xml
@@ -19,7 +19,7 @@
android:viewportHeight="24"
android:viewportWidth="24">
<path
- android:fillColor="#FFFFFF"
+ android:fillColor="?attr/pointerIconVectorStroke"
android:pathData="M13.945 13.829V10.17c1.476-.131 2.363-1.75 1.606-3.069l-1.758-3.065c-.793-1.383-2.786-1.383-3.579 0L8.455 7.102c-.757 1.319.131 2.939 1.607 3.069v3.658c-1.477.13-2.364 1.75-1.607 3.069l1.758 3.065c.793 1.383 2.786 1.383 3.579 0l1.758-3.065c.758-1.319-.129-2.938-1.605-3.069m.739 2.572-1.758 3.065a1.063 1.063 0 0 1-1.845 0l-1.758-3.065a1.065 1.065 0 0 1 .922-1.596h.818v-5.61h-.818c-.818 0-1.33-.886-.922-1.596l1.758-3.065a1.063 1.063 0 0 1 1.845 0l1.758 3.065a1.065 1.065 0 0 1-.922 1.596h-.817v5.609h.817c.817.001 1.329.886.922 1.597" />
<path
android:fillColor="?attr/pointerIconVectorFill"
diff --git a/core/res/res/drawable/pointer_vertical_text_vector.xml b/core/res/res/drawable/pointer_vertical_text_vector.xml
index 9238f94..d610d04 100644
--- a/core/res/res/drawable/pointer_vertical_text_vector.xml
+++ b/core/res/res/drawable/pointer_vertical_text_vector.xml
@@ -22,6 +22,6 @@
android:fillColor="?attr/pointerIconVectorFill"
android:pathData="M19 11H5a1 1 0 0 0 0 2h14a1 1 0 0 0 0-2" />
<path
- android:fillColor="#FFFFFF"
+ android:fillColor="?attr/pointerIconVectorStroke"
android:pathData="M19 10H5c-1.103 0-2 .897-2 2s.897 2 2 2h14c1.103 0 2-.897 2-2s-.897-2-2-2m0 3H5a1 1 0 0 1 0-2h14a1 1 0 0 1 0 2" />
</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_zoom_in_vector.xml b/core/res/res/drawable/pointer_zoom_in_vector.xml
index a7f56c2..c4aa95b 100644
--- a/core/res/res/drawable/pointer_zoom_in_vector.xml
+++ b/core/res/res/drawable/pointer_zoom_in_vector.xml
@@ -19,8 +19,8 @@
android:viewportHeight="24"
android:viewportWidth="24">
<group>
- <path android:fillColor="#FFFFFF" android:pathData="m20.445 17.298-3.591-3.613a7.5 7.5 0 0 0 1.243-4.138 7.547 7.547 0 1 0-7.546 7.546c1.239 0 2.402-.31 3.435-.84l3.733 3.756a1.922 1.922 0 1 0 2.726-2.711m-.713 2.009a.923.923 0 0 1-1.305-.004l-4.268-4.294a6.547 6.547 0 1 1 2.938-5.462 6.52 6.52 0 0 1-1.555 4.236l4.194 4.22a.92.92 0 0 1-.004 1.304" />
- <path android:fillColor="#FFFFFF" android:pathData="M10.55 5a4.546 4.546 0 1 0 0 9.093 4.546 4.546 0 0 0 0-9.093m2.462 5h-2v2a.5.5 0 0 1-1 0v-2h-2a.5.5 0 0 1 0-1h2V7a.5.5 0 0 1 1 0v2h2a.5.5 0 0 1 0 1" />
+ <path android:fillColor="?attr/pointerIconVectorStroke" android:pathData="m20.445 17.298-3.591-3.613a7.5 7.5 0 0 0 1.243-4.138 7.547 7.547 0 1 0-7.546 7.546c1.239 0 2.402-.31 3.435-.84l3.733 3.756a1.922 1.922 0 1 0 2.726-2.711m-.713 2.009a.923.923 0 0 1-1.305-.004l-4.268-4.294a6.547 6.547 0 1 1 2.938-5.462 6.52 6.52 0 0 1-1.555 4.236l4.194 4.22a.92.92 0 0 1-.004 1.304" />
+ <path android:fillColor="?attr/pointerIconVectorStroke" android:pathData="M10.55 5a4.546 4.546 0 1 0 0 9.093 4.546 4.546 0 0 0 0-9.093m2.462 5h-2v2a.5.5 0 0 1-1 0v-2h-2a.5.5 0 0 1 0-1h2V7a.5.5 0 0 1 1 0v2h2a.5.5 0 0 1 0 1" />
</group>
<group>
<path android:fillColor="?attr/pointerIconVectorFill" android:pathData="m19.736 18.003-4.194-4.22a6.547 6.547 0 1 0-1.382 1.226l4.268 4.294a.923.923 0 0 0 1.308-1.3m-9.186-3.91A4.546 4.546 0 1 1 10.549 5a4.546 4.546 0 0 1 .001 9.093" />
diff --git a/core/res/res/drawable/pointer_zoom_out_vector.xml b/core/res/res/drawable/pointer_zoom_out_vector.xml
index e46b978..16c4520 100644
--- a/core/res/res/drawable/pointer_zoom_out_vector.xml
+++ b/core/res/res/drawable/pointer_zoom_out_vector.xml
@@ -19,8 +19,8 @@
android:viewportHeight="24"
android:viewportWidth="24">
<group>
- <path android:fillColor="#FFFFFF" android:pathData="m20.445 17.298-3.591-3.613a7.5 7.5 0 0 0 1.243-4.138 7.547 7.547 0 1 0-7.546 7.546c1.239 0 2.402-.31 3.435-.84l3.733 3.756a1.922 1.922 0 1 0 2.726-2.711m-.713 2.009a.923.923 0 0 1-1.305-.004l-4.268-4.294a6.547 6.547 0 1 1 2.938-5.462 6.52 6.52 0 0 1-1.555 4.236l4.194 4.22a.92.92 0 0 1-.004 1.304" />
- <path android:fillColor="#FFFFFF" android:pathData="M10.55 5a4.546 4.546 0 1 0 0 9.093 4.546 4.546 0 0 0 0-9.093m2.462 5h-5a.5.5 0 0 1 0-1h5a.5.5 0 0 1 0 1" />
+ <path android:fillColor="?attr/pointerIconVectorStroke" android:pathData="m20.445 17.298-3.591-3.613a7.5 7.5 0 0 0 1.243-4.138 7.547 7.547 0 1 0-7.546 7.546c1.239 0 2.402-.31 3.435-.84l3.733 3.756a1.922 1.922 0 1 0 2.726-2.711m-.713 2.009a.923.923 0 0 1-1.305-.004l-4.268-4.294a6.547 6.547 0 1 1 2.938-5.462 6.52 6.52 0 0 1-1.555 4.236l4.194 4.22a.92.92 0 0 1-.004 1.304" />
+ <path android:fillColor="?attr/pointerIconVectorStroke" android:pathData="M10.55 5a4.546 4.546 0 1 0 0 9.093 4.546 4.546 0 0 0 0-9.093m2.462 5h-5a.5.5 0 0 1 0-1h5a.5.5 0 0 1 0 1" />
</group>
<group>
<path android:fillColor="?attr/pointerIconVectorFill" android:pathData="m19.736 18.003-4.194-4.22a6.547 6.547 0 1 0-1.382 1.226l4.268 4.294a.923.923 0 0 0 1.308-1.3m-9.186-3.91A4.546 4.546 0 1 1 10.549 5a4.546 4.546 0 0 1 .001 9.093" />
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index ea993ee..4892f59 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -10042,6 +10042,8 @@
<declare-styleable name="PointerIconVectorTheme">
<attr name="pointerIconVectorFill" format="color" />
<attr name="pointerIconVectorFillInverse" format="color" />
+ <attr name="pointerIconVectorStroke" format="color" />
+ <attr name="pointerIconVectorStrokeInverse" format="color" />
</declare-styleable>
<declare-styleable name="Storage">
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 335b740..ca80e22 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4136,6 +4136,9 @@
<!-- The default value for keyboard vibration toggle in settings. -->
<bool name="config_defaultKeyboardVibrationEnabled">true</bool>
+ <!-- Indicating if keyboard vibration settings supported or not. -->
+ <bool name="config_keyboardVibrationSettingsSupported">false</bool>
+
<!-- If the device should still vibrate even in low power mode, for certain priority vibrations
(e.g. accessibility, alarms). This is mainly for Wear devices that don't have speakers. -->
<bool name="config_allowPriorityVibrationsInLowPowerMode">false</bool>
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index 61c7a8c..cdd8557 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -393,6 +393,12 @@
<bool name="config_wait_for_device_alignment_in_demo_datagram">false</bool>
<java-symbol type="bool" name="config_wait_for_device_alignment_in_demo_datagram" />
+ <!-- Boolean indicating whether to enable MMS to be attempted on IWLAN if possible, even if
+ existing cellular networks already supports IWLAN.
+ -->
+ <bool name="force_iwlan_mms_feature_enabled">false</bool>
+ <java-symbol type="bool" name="force_iwlan_mms_feature_enabled" />
+
<!-- The time duration in millis after which Telephony will abort the last message datagram
sending requests. Telephony starts a timer when receiving a last message datagram sending
request in either OFF, IDLE, or NOT_CONNECTED state. In NOT_CONNECTED, the duration of the
diff --git a/core/res/res/values/locale_config.xml b/core/res/res/values/locale_config.xml
index bd93aa9..77d2e87 100644
--- a/core/res/res/values/locale_config.xml
+++ b/core/res/res/values/locale_config.xml
@@ -14,6 +14,11 @@
limitations under the License.
-->
+<!--
+ ICU Version: 75.1
+ CLDR Data Version: 45
+-->
+
<resources>
<string-array translatable="false" name="supported_locales">
@@ -88,6 +93,11 @@
<item>bem-ZM</item> <!-- Bemba (Zambia) -->
<item>bez-TZ</item> <!-- Bena (Tanzania) -->
<item>bg-BG</item> <!-- Bulgarian (Bulgaria) -->
+ <item>bgc-IN</item> <!-- Haryanvi (India) -->
+ <item>bgc-IN-u-nu-latn</item> <!-- Haryanvi (India, Western Digits) -->
+ <item>bho-IN</item> <!-- Bhojpuri (India) -->
+ <item>bho-IN-u-nu-latn</item> <!-- Bhojpuri (India, Western Digits) -->
+ <item>blo-BJ</item> <!-- Anii (Benin) -->
<item>bm-ML</item> <!-- Bambara (Mali) -->
<item>bn-BD</item> <!-- Bangla (Bangladesh) -->
<item>bn-BD-u-nu-latn</item> <!-- Bangla (Bangladesh, Western Digits) -->
@@ -108,6 +118,7 @@
<item>cgg-UG</item> <!-- Chiga (Uganda) -->
<item>chr-US</item> <!-- Cherokee (United States) -->
<item>cs-CZ</item> <!-- Czech (Czechia) -->
+ <item>csw-CA</item> <!-- Swampy Cree (Canada) -->
<item>cv-RU</item> <!-- Chuvash (Russia) -->
<item>cy-GB</item> <!-- Welsh (United Kingdom) -->
<item>da-DK</item> <!-- Danish (Denmark) -->
@@ -170,6 +181,7 @@
<item>en-GU</item> <!-- English (Guam) -->
<item>en-GY</item> <!-- English (Guyana) -->
<item>en-HK</item> <!-- English (Hong Kong) -->
+ <item>en-ID</item> <!-- English (Indonesia) -->
<item>en-IE</item> <!-- English (Ireland) -->
<item>en-IL</item> <!-- English (Israel) -->
<item>en-IM</item> <!-- English (Isle of Man) -->
@@ -237,6 +249,7 @@
<item>en-ZA</item> <!-- English (South Africa) -->
<item>en-ZM</item> <!-- English (Zambia) -->
<item>en-ZW</item> <!-- English (Zimbabwe) -->
+ <item>eo-001</item> <!-- Esperanto (world) -->
<item>es-AR</item> <!-- Spanish (Argentina) -->
<item>es-BO</item> <!-- Spanish (Bolivia) -->
<item>es-BR</item> <!-- Spanish (Brazil) -->
@@ -381,6 +394,7 @@
<item>hu-HU</item> <!-- Hungarian (Hungary) -->
<item>hy-AM</item> <!-- Armenian (Armenia) -->
<item>ia-001</item> <!-- Interlingua (world) -->
+ <item>ie-EE</item> <!-- Interlingue (Estonia) -->
<item>ig-NG</item> <!-- Igbo (Nigeria) -->
<item>ii-CN</item> <!-- Sichuan Yi (China) -->
<item>in-ID</item> <!-- Indonesian (Indonesia) -->
@@ -408,6 +422,7 @@
<item>kln-KE</item> <!-- Kalenjin (Kenya) -->
<item>km-KH</item> <!-- Khmer (Cambodia) -->
<item>kn-IN</item> <!-- Kannada (India) -->
+ <item>ko-CN</item> <!-- Korean (China) -->
<item>ko-KP</item> <!-- Korean (North Korea) -->
<item>ko-KR</item> <!-- Korean (South Korea) -->
<item>kok-IN</item> <!-- Konkani (India) -->
@@ -417,12 +432,19 @@
<item>ksb-TZ</item> <!-- Shambala (Tanzania) -->
<item>ksf-CM</item> <!-- Bafia (Cameroon) -->
<item>ksh-DE</item> <!-- Colognian (Germany) -->
+ <item>ku-TR</item> <!-- Kurdish (Türkiye) -->
<item>kw-GB</item> <!-- Cornish (United Kingdom) -->
+ <item>kxv-Deva-IN</item> <!-- Kuvi (Devanagari, India) -->
+ <item>kxv-Latn-IN</item> <!-- Kuvi (Latin, India) -->
+ <item>kxv-Orya-IN</item> <!-- Kuvi (Odia, India) -->
+ <item>kxv-Telu-IN</item> <!-- Kuvi (Telugu, India) -->
<item>ky-KG</item> <!-- Kyrgyz (Kyrgyzstan) -->
<item>lag-TZ</item> <!-- Langi (Tanzania) -->
<item>lb-LU</item> <!-- Luxembourgish (Luxembourg) -->
<item>lg-UG</item> <!-- Ganda (Uganda) -->
+ <item>lij-IT</item> <!-- Ligurian (Italy) -->
<item>lkt-US</item> <!-- Lakota (United States) -->
+ <item>lmo-IT</item> <!-- Lombard (Italy) -->
<item>ln-AO</item> <!-- Lingala (Angola) -->
<item>ln-CD</item> <!-- Lingala (Congo - Kinshasa) -->
<item>ln-CF</item> <!-- Lingala (Central African Republic) -->
@@ -462,6 +484,8 @@
<item>nb-NO</item> <!-- Norwegian Bokmål (Norway) -->
<item>nb-SJ</item> <!-- Norwegian Bokmål (Svalbard & Jan Mayen) -->
<item>nd-ZW</item> <!-- North Ndebele (Zimbabwe) -->
+ <item>nds-DE</item> <!-- Low German (Germany) -->
+ <item>nds-NL</item> <!-- Low German (Netherlands) -->
<item>ne-IN</item> <!-- Nepali (India) -->
<item>ne-IN-u-nu-latn</item> <!-- Nepali (India, Western Digits) -->
<item>ne-NP</item> <!-- Nepali (Nepal) -->
@@ -475,8 +499,12 @@
<item>nl-SX</item> <!-- Dutch (Sint Maarten) -->
<item>nn-NO</item> <!-- Norwegian Nynorsk (Norway) -->
<item>nnh-CM</item> <!-- Ngiemboon (Cameroon) -->
+ <item>nqo-GN</item> <!-- N’Ko (Guinea) -->
+ <item>nqo-GN-u-nu-latn</item> <!-- N’Ko (Guinea, Western Digits) -->
<item>nus-SS</item> <!-- Nuer (South Sudan) -->
<item>nyn-UG</item> <!-- Nyankole (Uganda) -->
+ <item>oc-ES</item> <!-- Occitan (Spain) -->
+ <item>oc-FR</item> <!-- Occitan (France) -->
<item>om-ET</item> <!-- Oromo (Ethiopia) -->
<item>om-KE</item> <!-- Oromo (Kenya) -->
<item>or-IN</item> <!-- Odia (India) -->
@@ -487,6 +515,7 @@
<item>pa-Guru-IN</item> <!-- Punjabi (Gurmukhi, India) -->
<item>pcm-NG</item> <!-- Nigerian Pidgin (Nigeria) -->
<item>pl-PL</item> <!-- Polish (Poland) -->
+ <item>prg-PL</item> <!-- Prussian (Poland) -->
<item>ps-AF</item> <!-- Pashto (Afghanistan) -->
<item>ps-AF-u-nu-latn</item> <!-- Pashto (Afghanistan, Western Digits) -->
<item>ps-PK</item> <!-- Pashto (Pakistan) -->
@@ -566,6 +595,9 @@
<item>sw-KE</item> <!-- Swahili (Kenya) -->
<item>sw-TZ</item> <!-- Swahili (Tanzania) -->
<item>sw-UG</item> <!-- Swahili (Uganda) -->
+ <item>syr-IQ</item> <!-- Syriac (Iraq) -->
+ <item>syr-SY</item> <!-- Syriac (Syria) -->
+ <item>szl-PL</item> <!-- Silesian (Poland) -->
<item>ta-IN</item> <!-- Tamil (India) -->
<item>ta-LK</item> <!-- Tamil (Sri Lanka) -->
<item>ta-MY</item> <!-- Tamil (Malaysia) -->
@@ -580,7 +612,7 @@
<item>tk-TM</item> <!-- Turkmen (Turkmenistan) -->
<item>to-TO</item> <!-- Tongan (Tonga) -->
<item>tr-CY</item> <!-- Turkish (Cyprus) -->
- <item>tr-TR</item> <!-- Turkish (Turkey) -->
+ <item>tr-TR</item> <!-- Turkish (Türkiye) -->
<item>tt-RU</item> <!-- Tatar (Russia) -->
<item>twq-NE</item> <!-- Tasawaq (Niger) -->
<item>tzm-MA</item> <!-- Central Atlas Tamazight (Morocco) -->
@@ -594,11 +626,14 @@
<item>uz-Arab-AF-u-nu-latn</item> <!-- Uzbek (Arabic, Afghanistan, Western Digits) -->
<item>uz-Cyrl-UZ</item> <!-- Uzbek (Cyrillic, Uzbekistan) -->
<item>uz-Latn-UZ</item> <!-- Uzbek (Latin, Uzbekistan) -->
+ <item>vec-IT</item> <!-- Venetian (Italy) -->
<item>vi-VN</item> <!-- Vietnamese (Vietnam) -->
+ <item>vmw-MZ</item> <!-- Makhuwa (Mozambique) -->
<item>vun-TZ</item> <!-- Vunjo (Tanzania) -->
<item>wae-CH</item> <!-- Walser (Switzerland) -->
<item>wo-SN</item> <!-- Wolof (Senegal) -->
<item>xh-ZA</item> <!-- Xhosa (South Africa) -->
+ <item>xnr-IN</item> <!-- Kangri (India) -->
<item>xog-UG</item> <!-- Soga (Uganda) -->
<item>yav-CM</item> <!-- Yangben (Cameroon) -->
<item>yo-BJ</item> <!-- Yoruba (Benin) -->
@@ -608,6 +643,7 @@
<item>yrl-VE</item> <!-- Nheengatu (Venezuela) -->
<item>yue-Hans-CN</item> <!-- Cantonese (Simplified, China) -->
<item>yue-Hant-HK</item> <!-- Cantonese (Traditional, Hong Kong) -->
+ <item>za-CN</item> <!-- Zhuang (China) -->
<item>zgh-MA</item> <!-- Standard Moroccan Tamazight (Morocco) -->
<item>zh-Hans-CN</item> <!-- Chinese (Simplified, China) -->
<item>zh-Hans-HK</item> <!-- Chinese (Simplified, Hong Kong) -->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 50c3b1a..aabc8ca 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1527,6 +1527,24 @@
</style>
<!-- @hide -->
+ <style name="PointerIconVectorStyleStrokeWhite">
+ <item name="pointerIconVectorStroke">@color/white</item>
+ <item name="pointerIconVectorStrokeInverse">@color/black</item>
+ </style>
+
+ <!-- @hide -->
+ <style name="PointerIconVectorStyleStrokeBlack">
+ <item name="pointerIconVectorStroke">@color/black</item>
+ <item name="pointerIconVectorStrokeInverse">@color/white</item>
+ </style>
+
+ <!-- @hide -->
+ <style name="PointerIconVectorStyleStrokeNone">
+ <item name="pointerIconVectorStroke">@color/transparent</item>
+ <item name="pointerIconVectorStrokeInverse">@color/transparent</item>
+ </style>
+
+ <!-- @hide -->
<style name="aerr_list_item" parent="Widget.Material.Light.Button.Borderless">
<item name="minHeight">?attr/listPreferredItemHeightSmall</item>
<item name="textAppearance">?attr/textAppearanceListItemSmall</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7d50d22..9661b46 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1704,6 +1704,10 @@
<java-symbol type="style" name="PointerIconVectorStyleFillPink" />
<java-symbol type="style" name="PointerIconVectorStyleFillBlue" />
<java-symbol type="attr" name="pointerIconVectorFill" />
+ <java-symbol type="style" name="PointerIconVectorStyleStrokeWhite" />
+ <java-symbol type="style" name="PointerIconVectorStyleStrokeBlack" />
+ <java-symbol type="style" name="PointerIconVectorStyleStrokeNone" />
+ <java-symbol type="attr" name="pointerIconVectorStroke" />
<java-symbol type="style" name="TextAppearance.DeviceDefault.Notification.Title" />
<java-symbol type="style" name="TextAppearance.DeviceDefault.Notification.Info" />
@@ -2122,6 +2126,7 @@
<java-symbol type="dimen" name="config_hapticChannelMaxVibrationAmplitude" />
<java-symbol type="dimen" name="config_keyboardHapticFeedbackFixedAmplitude" />
<java-symbol type="bool" name="config_defaultKeyboardVibrationEnabled" />
+ <java-symbol type="bool" name="config_keyboardVibrationSettingsSupported" />
<java-symbol type="integer" name="config_vibrationWaveformRampStepDuration" />
<java-symbol type="bool" name="config_ignoreVibrationsOnWirelessCharger" />
<java-symbol type="integer" name="config_vibrationWaveformRampDownDuration" />
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
index 612b387..9ea2943 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -50,6 +50,7 @@
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.window.flags.Flags;
import java.util.Map;
import java.util.concurrent.Executor;
@@ -393,19 +394,31 @@
if (splitAttributes == null) {
return TaskFragmentAnimationParams.DEFAULT;
}
+ final TaskFragmentAnimationParams.Builder builder =
+ new TaskFragmentAnimationParams.Builder();
final int animationBackgroundColor = getAnimationBackgroundColor(splitAttributes);
- TaskFragmentAnimationParams.Builder builder = new TaskFragmentAnimationParams.Builder();
- if (animationBackgroundColor != DEFAULT_ANIMATION_BACKGROUND_COLOR) {
- builder.setAnimationBackgroundColor(animationBackgroundColor);
+ builder.setAnimationBackgroundColor(animationBackgroundColor);
+ if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
+ final int openAnimationResId =
+ splitAttributes.getAnimationParams().getOpenAnimationResId();
+ builder.setOpenAnimationResId(openAnimationResId);
+ final int closeAnimationResId =
+ splitAttributes.getAnimationParams().getCloseAnimationResId();
+ builder.setCloseAnimationResId(closeAnimationResId);
+ final int changeAnimationResId =
+ splitAttributes.getAnimationParams().getChangeAnimationResId();
+ builder.setChangeAnimationResId(changeAnimationResId);
}
- // TODO(b/293658614): Allow setting custom open/close/changeAnimationResId.
return builder.build();
}
@ColorInt
private static int getAnimationBackgroundColor(@NonNull SplitAttributes splitAttributes) {
int animationBackgroundColor = DEFAULT_ANIMATION_BACKGROUND_COLOR;
- final AnimationBackground animationBackground = splitAttributes.getAnimationBackground();
+ AnimationBackground animationBackground = splitAttributes.getAnimationBackground();
+ if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
+ animationBackground = splitAttributes.getAnimationParams().getAnimationBackground();
+ }
if (animationBackground instanceof AnimationBackground.ColorBackground colorBackground) {
animationBackgroundColor = colorBackground.getColor();
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java
index 4267749..c5aaddc 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java
@@ -29,6 +29,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.window.extensions.embedding.AnimationBackground;
+import androidx.window.extensions.embedding.AnimationParams;
import androidx.window.extensions.embedding.SplitAttributes;
import org.junit.Before;
@@ -112,5 +113,13 @@
.isEqualTo(new SplitAttributes.SplitType.RatioSplitType(0.5f));
assertThat(splitAttributes.getAnimationBackground())
.isEqualTo(AnimationBackground.ANIMATION_BACKGROUND_DEFAULT);
+ assertThat(splitAttributes.getAnimationParams().getAnimationBackground())
+ .isEqualTo(AnimationBackground.ANIMATION_BACKGROUND_DEFAULT);
+ assertThat(splitAttributes.getAnimationParams().getOpenAnimationResId())
+ .isEqualTo(AnimationParams.DEFAULT_ANIMATION_RESOURCES_ID);
+ assertThat(splitAttributes.getAnimationParams().getCloseAnimationResId())
+ .isEqualTo(AnimationParams.DEFAULT_ANIMATION_RESOURCES_ID);
+ assertThat(splitAttributes.getAnimationParams().getChangeAnimationResId())
+ .isEqualTo(AnimationParams.DEFAULT_ANIMATION_RESOURCES_ID);
}
}
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/Android.bp b/libs/WindowManager/Shell/multivalentScreenshotTests/Android.bp
index c6dbd9b..1871203 100644
--- a/libs/WindowManager/Shell/multivalentScreenshotTests/Android.bp
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/Android.bp
@@ -22,6 +22,40 @@
default_team: "trendy_team_multitasking_windowing",
}
+android_app {
+ name: "WMShellRobolectricScreenshotTestApp",
+ platform_apis: true,
+ certificate: "platform",
+ static_libs: [
+ "WindowManager-Shell",
+ "platform-screenshot-diff-core",
+ ],
+ asset_dirs: ["goldens/robolectric"],
+ manifest: "AndroidManifestRobolectric.xml",
+ use_resource_processor: true,
+}
+
+android_robolectric_test {
+ name: "WMShellRobolectricScreenshotTests",
+ instrumentation_for: "WMShellRobolectricScreenshotTestApp",
+ upstream: true,
+ java_resource_dirs: [
+ "robolectric/config",
+ ],
+ srcs: [
+ "src/**/*.kt",
+ ],
+ static_libs: [
+ "junit",
+ "androidx.test.runner",
+ "androidx.test.rules",
+ "androidx.test.ext.junit",
+ "truth",
+ "platform-parametric-runner-lib",
+ ],
+ auto_gen_config: true,
+}
+
android_test {
name: "WMShellMultivalentScreenshotTestsOnDevice",
srcs: [
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidManifestRobolectric.xml b/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidManifestRobolectric.xml
index a7a3f13..b4bdaea 100644
--- a/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidManifestRobolectric.xml
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/AndroidManifestRobolectric.xml
@@ -16,7 +16,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.wm.shell.multivalentscreenshot">
<application android:debuggable="true" android:supportsRtl="true">
- <uses-library android:name="android.test.runner" />
<activity
android:name="platform.test.screenshot.ScreenshotActivity"
android:exported="true">
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.png
new file mode 100644
index 0000000..723c6b8
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.png
new file mode 100644
index 0000000..723c6b8
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/robolectric/config/robolectric.properties b/libs/WindowManager/Shell/multivalentScreenshotTests/robolectric/config/robolectric.properties
index 7a0527c..d50d976 100644
--- a/libs/WindowManager/Shell/multivalentScreenshotTests/robolectric/config/robolectric.properties
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/robolectric/config/robolectric.properties
@@ -1,2 +1,3 @@
sdk=NEWEST_SDK
+graphicsMode=NATIVE
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java
index 92084e4..67d46f4 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java
@@ -16,21 +16,19 @@
package com.android.wm.shell.shared;
-import static android.provider.Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES;
-
import android.annotation.NonNull;
import android.content.Context;
import android.os.SystemProperties;
-import android.provider.Settings;
-import android.util.Log;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.window.flags.Flags;
+import com.android.wm.shell.shared.desktopmode.DesktopModeFlags;
/**
* Constants for desktop mode feature
*/
+// TODO(b/237575897): Move this file to the `com.android.wm.shell.shared.desktopmode` package
public class DesktopModeStatus {
private static final String TAG = "DesktopModeStatus";
@@ -178,19 +176,7 @@
public static boolean canEnterDesktopMode(@NonNull Context context) {
if (!isDeviceEligibleForDesktopMode(context)) return false;
- // If dev option has ever been manually toggled by the user, return its value
- // TODO(b/348193756) : Move the logic for DW override based on toggle overides to a common
- // infrastructure and add caching for the computation
- int defaultOverrideState = -1;
- int toggleState = Settings.Global.getInt(context.getContentResolver(),
- DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES, defaultOverrideState);
- if (toggleState != defaultOverrideState) {
- Log.d(TAG, "Using Desktop mode dev option overridden state");
- return toggleState != 0;
- }
-
- // Return Desktop windowing flag value
- return isDesktopModeFlagEnabled();
+ return DesktopModeFlags.DESKTOP_WINDOWING_MODE.isEnabled(context);
}
/**
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt
new file mode 100644
index 0000000..8f7fdd6
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.shared.desktopmode
+
+import android.content.Context
+import android.provider.Settings
+import android.util.Log
+import com.android.window.flags.Flags
+import com.android.wm.shell.shared.DesktopModeStatus
+
+/*
+ * A shared class to check desktop mode flags state.
+ *
+ * The class computes whether a Desktop Windowing flag should be enabled by using the aconfig flag
+ * value and the developer option override state (if applicable).
+ **/
+enum class DesktopModeFlags(
+ // Function called to obtain aconfig flag value.
+ private val flagFunction: () -> Boolean,
+ // Whether the flag state should be affected by developer option.
+ private val shouldOverrideByDevOption: Boolean
+) {
+ // All desktop mode related flags will be added here
+ DESKTOP_WINDOWING_MODE(DesktopModeStatus::isDesktopModeFlagEnabled, true);
+
+ // Local cache for toggle override, which is initialized once on its first access. It needs to be
+ // refreshed only on reboots as overridden state takes effect on reboots.
+ private var cachedToggleOverride: ToggleOverride? = null
+
+ /**
+ * Determines state of flag based on the actual flag and desktop mode developer option overrides.
+ *
+ * Note, this method makes sure that a constant developer toggle overrides is read until reboot.
+ */
+ fun isEnabled(context: Context): Boolean =
+ if (!Flags.showDesktopWindowingDevOption() ||
+ !shouldOverrideByDevOption ||
+ context.contentResolver == null) {
+ flagFunction()
+ } else {
+ when (getToggleOverride(context)) {
+ ToggleOverride.OVERRIDE_UNSET -> flagFunction()
+ ToggleOverride.OVERRIDE_OFF -> false
+ ToggleOverride.OVERRIDE_ON -> true
+ }
+ }
+
+ private fun getToggleOverride(context: Context): ToggleOverride {
+ val override =
+ cachedToggleOverride
+ ?: run {
+ val override = getToggleOverrideFromSystem(context)
+ // Cache toggle override the first time we encounter context. Override does not change
+ // with context, as context is just used to fetch System Property and Settings.Global
+ cachedToggleOverride = override
+ Log.d(TAG, "Toggle override initialized to: $override")
+ override
+ }
+
+ return override
+ }
+
+ private fun getToggleOverrideFromSystem(context: Context): ToggleOverride {
+ // A non-persistent System Property is used to store override to ensure it remains
+ // constant till reboot.
+ val overrideFromSystemProperties: ToggleOverride? =
+ System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, null).convertToToggleOverride()
+ return overrideFromSystemProperties
+ ?: run {
+ // Read Setting Global if System Property is not present (just after reboot)
+ // or not valid (user manually changed the value)
+ val overrideFromSettingsGlobal =
+ Settings.Global.getInt(
+ context.contentResolver,
+ Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES,
+ ToggleOverride.OVERRIDE_UNSET.setting)
+ .convertToToggleOverrideWithFallback(ToggleOverride.OVERRIDE_UNSET)
+ // Initialize System Property
+ System.setProperty(
+ SYSTEM_PROPERTY_OVERRIDE_KEY, overrideFromSettingsGlobal.setting.toString())
+
+ overrideFromSettingsGlobal
+ }
+ }
+
+ // TODO(b/348193756): Share ToggleOverride enum with Settings 'DesktopModePreferenceController'
+ /**
+ * Override state of desktop mode developer option toggle.
+ *
+ * @property setting The integer value that is associated with the developer option toggle
+ * override
+ */
+ enum class ToggleOverride(val setting: Int) {
+ /** No override is set. */
+ OVERRIDE_UNSET(-1),
+ /** Override to off. */
+ OVERRIDE_OFF(0),
+ /** Override to on. */
+ OVERRIDE_ON(1)
+ }
+
+ private val settingToToggleOverrideMap = ToggleOverride.entries.associateBy { it.setting }
+
+ private fun String?.convertToToggleOverride(): ToggleOverride? {
+ val intValue = this?.toIntOrNull() ?: return null
+ return settingToToggleOverrideMap[intValue]
+ ?: run {
+ Log.w(TAG, "Unknown toggleOverride int $intValue")
+ null
+ }
+ }
+
+ private fun Int.convertToToggleOverrideWithFallback(
+ fallbackOverride: ToggleOverride
+ ): ToggleOverride {
+ return settingToToggleOverrideMap[this]
+ ?: run {
+ Log.w(TAG, "Unknown toggleOverride int $this")
+ fallbackOverride
+ }
+ }
+
+ private companion object {
+ const val TAG = "DesktopModeFlags"
+
+ /**
+ * Key for non-persistent System Property which is used to store desktop windowing developer
+ * option overrides.
+ */
+ const val SYSTEM_PROPERTY_OVERRIDE_KEY = "sys.wmshell.desktopmode.dev_toggle_override"
+ }
+}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/OWNERS b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/OWNERS
new file mode 100644
index 0000000..2fabd4a
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/OWNERS
@@ -0,0 +1 @@
+file:/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
index d270d2b..5696a54 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
@@ -266,6 +266,9 @@
final Animation animation =
animationProvider.get(info, change, openingWholeScreenBounds);
if (shouldUseJumpCutForAnimation(animation)) {
+ if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
+ return new ArrayList<>();
+ }
continue;
}
final ActivityEmbeddingAnimationAdapter adapter = createOpenCloseAnimationAdapter(
@@ -291,6 +294,9 @@
final Animation animation =
animationProvider.get(info, change, closingWholeScreenBounds);
if (shouldUseJumpCutForAnimation(animation)) {
+ if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
+ return new ArrayList<>();
+ }
continue;
}
final ActivityEmbeddingAnimationAdapter adapter = createOpenCloseAnimationAdapter(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
index f49b90d0..3046307 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
@@ -97,7 +97,7 @@
Animation createChangeBoundsOpenAnimation(@NonNull TransitionInfo info,
@NonNull TransitionInfo.Change change, @NonNull Rect parentBounds) {
if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
- final Animation customAnimation = loadCustomAnimation(info, change);
+ final Animation customAnimation = loadCustomAnimation(info, change, TRANSIT_CHANGE);
if (customAnimation != null) {
return customAnimation;
}
@@ -131,7 +131,7 @@
Animation createChangeBoundsCloseAnimation(@NonNull TransitionInfo info,
@NonNull TransitionInfo.Change change, @NonNull Rect parentBounds) {
if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
- final Animation customAnimation = loadCustomAnimation(info, change);
+ final Animation customAnimation = loadCustomAnimation(info, change, TRANSIT_CHANGE);
if (customAnimation != null) {
return customAnimation;
}
@@ -172,7 +172,7 @@
// TODO(b/293658614): Support more complicated animations that may need more than a noop
// animation as the start leash.
final Animation noopAnimation = createNoopAnimation(change);
- final Animation customAnimation = loadCustomAnimation(info, change);
+ final Animation customAnimation = loadCustomAnimation(info, change, TRANSIT_CHANGE);
if (customAnimation != null) {
return new Animation[]{noopAnimation, customAnimation};
}
@@ -227,7 +227,7 @@
Animation loadOpenAnimation(@NonNull TransitionInfo info,
@NonNull TransitionInfo.Change change, @NonNull Rect wholeAnimationBounds) {
final boolean isEnter = TransitionUtil.isOpeningType(change.getMode());
- final Animation customAnimation = loadCustomAnimation(info, change);
+ final Animation customAnimation = loadCustomAnimation(info, change, change.getMode());
final Animation animation;
if (customAnimation != null) {
animation = customAnimation;
@@ -254,7 +254,7 @@
Animation loadCloseAnimation(@NonNull TransitionInfo info,
@NonNull TransitionInfo.Change change, @NonNull Rect wholeAnimationBounds) {
final boolean isEnter = TransitionUtil.isOpeningType(change.getMode());
- final Animation customAnimation = loadCustomAnimation(info, change);
+ final Animation customAnimation = loadCustomAnimation(info, change, change.getMode());
final Animation animation;
if (customAnimation != null) {
animation = customAnimation;
@@ -287,14 +287,14 @@
@Nullable
private Animation loadCustomAnimation(@NonNull TransitionInfo info,
- @NonNull TransitionInfo.Change change) {
+ @NonNull TransitionInfo.Change change, @WindowManager.TransitionType int mode) {
final TransitionInfo.AnimationOptions options;
if (Flags.moveAnimationOptionsToChange()) {
options = change.getAnimationOptions();
} else {
options = info.getAnimationOptions();
}
- return loadCustomAnimationFromOptions(options, change.getMode());
+ return loadCustomAnimationFromOptions(options, mode);
}
@Nullable
@@ -319,8 +319,14 @@
return null;
}
- final Animation anim = mTransitionAnimation.loadAnimationRes(options.getPackageName(),
- resId);
+ final Animation anim;
+ if (Flags.activityEmbeddingAnimationCustomizationFlag()) {
+ // TODO(b/293658614): Consider allowing custom animations from non-default packages.
+ // Enforce limiting to animations from the default "android" package for now.
+ anim = mTransitionAnimation.loadDefaultAnimationRes(resId);
+ } else {
+ anim = mTransitionAnimation.loadAnimationRes(options.getPackageName(), resId);
+ }
if (anim != null) {
return anim;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt
index 6781d08..d1b2347 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/AppCompatUtils.kt
@@ -19,6 +19,16 @@
package com.android.wm.shell.compatui
import android.app.TaskInfo
-fun isSingleTopActivityTranslucent(task: TaskInfo) =
- task.isTopActivityTransparent && task.numActivities == 1
+import android.content.Context
+import com.android.internal.R
+// TODO(b/347289970): Consider replacing with API
+fun isTopActivityExemptFromDesktopWindowing(context: Context, task: TaskInfo) =
+ isSystemUiTask(context, task) || (task.isTopActivityTransparent && task.numActivities == 1
+ && !task.isTopActivityStyleFloating)
+
+private fun isSystemUiTask(context: Context, task: TaskInfo): Boolean {
+ val sysUiPackageName: String =
+ context.resources.getString(R.string.config_systemUi)
+ return task.baseActivity?.packageName == sysUiPackageName
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index da1d6da..e792f7a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -24,7 +24,6 @@
import android.os.UserManager;
import android.view.Choreographer;
import android.view.IWindowManager;
-import android.view.SurfaceControl;
import android.view.WindowManager;
import com.android.internal.jank.InteractionJankMonitor;
@@ -404,8 +403,7 @@
Optional<RecentTasksController> recentTasksController,
HomeTransitionObserver homeTransitionObserver) {
return new RecentsTransitionHandler(shellInit, transitions,
- recentTasksController.orElse(null), homeTransitionObserver,
- SurfaceControl.Transaction::new);
+ recentTasksController.orElse(null), homeTransitionObserver);
}
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
index 81891ce..df79b15 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
@@ -250,6 +250,10 @@
return ArraySet(displayData[displayId]?.activeTasks)
}
+ /** Returns the minimized tasks for the given [displayId]. */
+ fun getMinimizedTasks(displayId: Int): ArraySet<Int> =
+ ArraySet(displayData[displayId]?.minimizedTasks)
+
/**
* Returns whether Desktop Mode is currently showing any tasks, i.e. whether any Desktop Tasks
* are visible.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 7cc01d5..e247912 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -66,7 +66,7 @@
import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource
import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
-import com.android.wm.shell.compatui.isSingleTopActivityTranslucent
+import com.android.wm.shell.compatui.isTopActivityExemptFromDesktopWindowing
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksListener
import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.DragToDesktopStateListener
import com.android.wm.shell.draganddrop.DragAndDropController
@@ -158,8 +158,6 @@
visualIndicator = null
}
}
- private val sysUIPackageName = context.resources.getString(
- com.android.internal.R.string.config_systemUi)
private val transitionAreaHeight
get() =
@@ -219,11 +217,6 @@
return visualIndicator
}
- // TODO(b/347289970): Consider replacing with API
- private fun isSystemUIApplication(taskInfo: RunningTaskInfo): Boolean {
- return taskInfo.baseActivity?.packageName == sysUIPackageName
- }
-
fun setOnTaskResizeAnimationListener(listener: OnTaskResizeAnimationListener) {
toggleResizeDesktopTaskTransitionHandler.setOnTaskResizeAnimationListener(listener)
enterDesktopTaskTransitionHandler.setOnTaskResizeAnimationListener(listener)
@@ -351,19 +344,12 @@
wct: WindowContainerTransaction = WindowContainerTransaction(),
transitionSource: DesktopModeTransitionSource,
) {
- if (Flags.enableDesktopWindowingModalsPolicy() && isSingleTopActivityTranslucent(task)) {
+ if (Flags.enableDesktopWindowingModalsPolicy()
+ && isTopActivityExemptFromDesktopWindowing(context, task)) {
KtProtoLog.w(
WM_SHELL_DESKTOP_MODE,
"DesktopTasksController: Cannot enter desktop, " +
- "translucent top activity found. This is likely a modal dialog."
- )
- return
- }
- if (isSystemUIApplication(task)) {
- KtProtoLog.w(
- WM_SHELL_DESKTOP_MODE,
- "DesktopTasksController: Cannot enter desktop, " +
- "systemUI top activity found."
+ "ineligible top activity found."
)
return
}
@@ -417,7 +403,7 @@
)
val wct = WindowContainerTransaction()
exitSplitIfApplicable(wct, taskInfo)
- moveHomeTask(wct, true /* toTop */)
+ moveHomeTask(wct, toTop = true)
val taskToMinimize =
bringDesktopAppsToFrontBeforeShowingNewTask(taskInfo.displayId, wct, taskInfo.taskId)
addMoveToDesktopChanges(wct, taskInfo)
@@ -795,7 +781,7 @@
addWallpaperActivity(wct)
} else {
// Move home to front
- moveHomeTask(wct, true /* toTop */)
+ moveHomeTask(wct, toTop = true)
}
val nonMinimizedTasksOrderedFrontToBack =
@@ -942,10 +928,8 @@
when {
// Check if the closing task needs to be handled
TransitionUtil.isClosingType(request.type) -> handleTaskClosing(task)
- // Check if the task has a top transparent activity
- shouldLaunchAsModal(task) -> handleIncompatibleTaskLaunch(task)
- // Check if the task has a top systemUI activity
- isSystemUIApplication(task) -> handleIncompatibleTaskLaunch(task)
+ // Check if the top task shouldn't be allowed to enter desktop mode
+ isIncompatibleTask(task) -> handleIncompatibleTaskLaunch(task)
// Check if fullscreen task should be updated
task.isFullscreen -> handleFullscreenTaskLaunch(task, transition)
// Check if freeform task should be updated
@@ -979,9 +963,9 @@
.forEach { finishTransaction.setCornerRadius(it.leash, cornerRadius) }
}
- // TODO(b/347289970): Consider replacing with API
- private fun shouldLaunchAsModal(task: TaskInfo) =
- Flags.enableDesktopWindowingModalsPolicy() && isSingleTopActivityTranslucent(task)
+ private fun isIncompatibleTask(task: TaskInfo) =
+ Flags.enableDesktopWindowingModalsPolicy()
+ && isTopActivityExemptFromDesktopWindowing(context, task)
private fun shouldHandleTaskClosing(request: TransitionRequestInfo): Boolean {
return Flags.enableDesktopWindowingWallpaperActivity() &&
@@ -1042,7 +1026,7 @@
addMoveToDesktopChanges(wct, task)
// In some launches home task is moved behind new task being launched. Make sure
// that's not the case for launches in desktop.
- moveHomeTask(wct, false /* toTop */)
+ moveHomeTask(wct, toTop = false)
// Desktop Mode is already showing and we're launching a new Task - we might need to
// minimize another Task.
val taskToMinimize = addAndGetMinimizeChangesIfNeeded(task.displayId, wct, task)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
index 0f88384..c85f76d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
@@ -42,9 +42,12 @@
private val shellTaskOrganizer: ShellTaskOrganizer,
) {
private val minimizeTransitionObserver = MinimizeTransitionObserver()
+ @VisibleForTesting
+ val leftoverMinimizedTasksRemover = LeftoverMinimizedTasksRemover()
init {
transitions.registerObserver(minimizeTransitionObserver)
+ taskRepository.addActiveTaskListener(leftoverMinimizedTasksRemover)
}
private data class TaskDetails (val displayId: Int, val taskId: Int)
@@ -113,6 +116,35 @@
}
}
+ @VisibleForTesting
+ inner class LeftoverMinimizedTasksRemover : DesktopModeTaskRepository.ActiveTasksListener {
+ override fun onActiveTasksChanged(displayId: Int) {
+ val wct = WindowContainerTransaction()
+ removeLeftoverMinimizedTasks(displayId, wct)
+ shellTaskOrganizer.applyTransaction(wct)
+ }
+
+ fun removeLeftoverMinimizedTasks(displayId: Int, wct: WindowContainerTransaction) {
+ if (taskRepository
+ .getActiveNonMinimizedTasksOrderedFrontToBack(displayId).isNotEmpty()) {
+ return
+ }
+ val remainingMinimizedTasks = taskRepository.getMinimizedTasks(displayId)
+ if (remainingMinimizedTasks.isEmpty()) {
+ return
+ }
+ KtProtoLog.v(
+ ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksLimiter: removing leftover minimized tasks: $remainingMinimizedTasks")
+ remainingMinimizedTasks.forEach { taskIdToRemove ->
+ val taskToRemove = shellTaskOrganizer.getRunningTaskInfo(taskIdToRemove)
+ if (taskToRemove != null) {
+ wct.removeTask(taskToRemove.token)
+ }
+ }
+ }
+ }
+
/**
* Mark a task as minimized, this should only be done after the corresponding transition has
* finished so we don't minimize the task if the transition fails.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index e46625d..234b4d0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -74,7 +74,6 @@
import java.util.ArrayList;
import java.util.function.Consumer;
-import java.util.function.Supplier;
/**
* Handles the Recents (overview) animation. Only one of these can run at a time. A recents
@@ -85,7 +84,6 @@
private final Transitions mTransitions;
private final ShellExecutor mExecutor;
- private final Supplier<SurfaceControl.Transaction> mTransactionSupplier;
@Nullable
private final RecentTasksController mRecentTasksController;
private IApplicationThread mAnimApp = null;
@@ -103,13 +101,11 @@
public RecentsTransitionHandler(ShellInit shellInit, Transitions transitions,
@Nullable RecentTasksController recentTasksController,
- HomeTransitionObserver homeTransitionObserver,
- Supplier<SurfaceControl.Transaction> transactionSupplier) {
+ HomeTransitionObserver homeTransitionObserver) {
mTransitions = transitions;
mExecutor = transitions.getMainExecutor();
mRecentTasksController = recentTasksController;
mHomeTransitionObserver = homeTransitionObserver;
- mTransactionSupplier = transactionSupplier;
if (!Transitions.ENABLE_SHELL_TRANSITIONS) return;
if (recentTasksController == null) return;
shellInit.addInitCallback(() -> {
@@ -1060,7 +1056,7 @@
final Transitions.TransitionFinishCallback finishCB = mFinishCB;
mFinishCB = null;
- SurfaceControl.Transaction t = mFinishTransaction;
+ final SurfaceControl.Transaction t = mFinishTransaction;
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (mKeyguardLocked && mRecentsTask != null) {
@@ -1110,16 +1106,6 @@
}
}
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, " normal finish");
- if (toHome && !mOpeningTasks.isEmpty()) {
- // Attempting to start a task after swipe to home, don't show it,
- // move recents to top
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
- " attempting to start a task after swipe to home");
- t = mTransactionSupplier.get();
- wct.reorder(mRecentsTask, true /*onTop*/);
- mClosingTasks.addAll(mOpeningTasks);
- mOpeningTasks.clear();
- }
// The general case: committing to recents, going home, or switching tasks.
for (int i = 0; i < mOpeningTasks.size(); ++i) {
t.show(mOpeningTasks.get(i).mTaskSurface);
@@ -1188,10 +1174,6 @@
mPipTransaction = null;
}
}
- if (t != mFinishTransaction) {
- // apply after merges because these changes are accounting for finishWCT changes.
- mTransitions.setAfterMergeFinishTransaction(mTransition, t);
- }
cleanUp();
finishCB.onTransitionFinished(wct.isEmpty() ? null : wct);
if (runnerFinishCb != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index ec6802d..bd25846 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -28,6 +28,7 @@
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.fixScale;
+import static android.view.WindowManager.transitTypeToString;
import static android.window.TransitionInfo.FLAGS_IS_NON_APP_WINDOW;
import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW;
@@ -241,13 +242,6 @@
/** Ordered list of transitions which have been merged into this one. */
private ArrayList<ActiveTransition> mMerged;
- /**
- * @deprecated DO NOT USE THIS unless absolutely necessary. It will be removed once
- * everything migrates off finishWCT.
- */
- @java.lang.Deprecated
- SurfaceControl.Transaction mAfterMergeFinishT;
-
ActiveTransition(IBinder token) {
mToken = token;
}
@@ -1033,20 +1027,6 @@
return null;
}
- /** @deprecated */
- @java.lang.Deprecated
- public void setAfterMergeFinishTransaction(IBinder transition,
- SurfaceControl.Transaction afterMergeFinishT) {
- final ActiveTransition at = mKnownTransitions.get(transition);
- if (at == null) return;
- if (at.mAfterMergeFinishT != null) {
- Log.e(TAG, "Setting after-merge-t >1 time on transition: " + at.mInfo.getDebugId());
- at.mAfterMergeFinishT.merge(afterMergeFinishT);
- return;
- }
- at.mAfterMergeFinishT = afterMergeFinishT;
- }
-
/** Aborts a transition. This will still queue it up to maintain order. */
private void onAbort(ActiveTransition transition) {
final Track track = mTracks.get(transition.getTrack());
@@ -1107,7 +1087,6 @@
}
// Merge all associated transactions together
SurfaceControl.Transaction fullFinish = active.mFinishT;
- SurfaceControl.Transaction afterMergeFinish = active.mAfterMergeFinishT;
if (active.mMerged != null) {
for (int iM = 0; iM < active.mMerged.size(); ++iM) {
final ActiveTransition toMerge = active.mMerged.get(iM);
@@ -1127,21 +1106,6 @@
fullFinish.merge(toMerge.mFinishT);
}
}
- if (toMerge.mAfterMergeFinishT != null) {
- if (afterMergeFinish == null) {
- afterMergeFinish = toMerge.mAfterMergeFinishT;
- } else {
- afterMergeFinish.merge(toMerge.mAfterMergeFinishT);
- }
- toMerge.mAfterMergeFinishT = null;
- }
- }
- }
- if (afterMergeFinish != null) {
- if (fullFinish == null) {
- fullFinish = afterMergeFinish;
- } else {
- fullFinish.merge(afterMergeFinish);
}
}
if (fullFinish != null) {
@@ -1231,7 +1195,7 @@
public IBinder startTransition(@WindowManager.TransitionType int type,
@NonNull WindowContainerTransaction wct, @Nullable TransitionHandler handler) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Directly starting a new transition "
- + "type=%d wct=%s handler=%s", type, wct, handler);
+ + "type=%s wct=%s handler=%s", transitTypeToString(type), wct, handler);
final ActiveTransition active =
new ActiveTransition(mOrganizer.startNewTransition(type, wct));
active.mHandler = handler;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index e3aa31f..440fc4d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -32,7 +32,7 @@
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
-import static com.android.wm.shell.compatui.AppCompatUtils.isSingleTopActivityTranslucent;
+import static com.android.wm.shell.compatui.AppCompatUtils.isTopActivityExemptFromDesktopWindowing;
import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
@@ -105,7 +105,6 @@
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
import java.io.PrintWriter;
-import java.util.Objects;
import java.util.Optional;
import java.util.function.Supplier;
@@ -1034,12 +1033,8 @@
&& taskInfo.isFocused) {
return false;
}
- // TODO(b/347289970): Consider replacing with API
if (Flags.enableDesktopWindowingModalsPolicy()
- && isSingleTopActivityTranslucent(taskInfo)) {
- return false;
- }
- if (isSystemUIApplication(taskInfo)) {
+ && isTopActivityExemptFromDesktopWindowing(mContext, taskInfo)) {
return false;
}
return DesktopModeStatus.canEnterDesktopMode(mContext)
@@ -1118,14 +1113,6 @@
&& mSplitScreenController.isTaskInSplitScreen(taskId);
}
- // TODO(b/347289970): Consider replacing with API
- private boolean isSystemUIApplication(RunningTaskInfo taskInfo) {
- if (taskInfo.baseActivity != null) {
- return (Objects.equals(taskInfo.baseActivity.getPackageName(), mSysUIPackageName));
- }
- return false;
- }
-
private void dump(PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.println(prefix + "DesktopModeWindowDecorViewModel");
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
index a0a61fe2..d0e8215 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
@@ -117,12 +117,10 @@
/**
* Checks that all parts of the screen are covered at the start and end of the transition
- *
- * TODO b/197726599 Prevents all states from being checked
*/
@Presubmit
@Test
- fun entireScreenCoveredAtStartAndEnd() = flicker.entireScreenCovered(allStates = false)
+ fun entireScreenCoveredAtStartAndEnd() = flicker.entireScreenCovered()
/** Checks [pipApp] window remains visible and on top throughout the transition */
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt
index 4cd2a36..ecaf970 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt
@@ -16,8 +16,10 @@
package com.android.wm.shell.compatui
+import android.content.ComponentName
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
+import com.android.internal.R
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
import org.junit.Assert.assertFalse
@@ -34,26 +36,55 @@
@RunWith(AndroidTestingRunner::class)
@SmallTest
class AppCompatUtilsTest : ShellTestCase() {
-
@Test
- fun testIsSingleTopActivityTranslucent() {
- assertTrue(isSingleTopActivityTranslucent(
+ fun testIsTopActivityExemptFromDesktopWindowing_topActivityTransparent() {
+ assertTrue(isTopActivityExemptFromDesktopWindowing(mContext,
createFreeformTask(/* displayId */ 0)
.apply {
isTopActivityTransparent = true
numActivities = 1
}))
- assertFalse(isSingleTopActivityTranslucent(
+ assertFalse(isTopActivityExemptFromDesktopWindowing(mContext,
createFreeformTask(/* displayId */ 0)
.apply {
isTopActivityTransparent = true
numActivities = 0
}))
- assertFalse(isSingleTopActivityTranslucent(
+ }
+
+ @Test
+ fun testIsTopActivityExemptFromDesktopWindowing_singleTopActivity() {
+ assertTrue(isTopActivityExemptFromDesktopWindowing(mContext,
+ createFreeformTask(/* displayId */ 0)
+ .apply {
+ isTopActivityTransparent = true
+ numActivities = 1
+ }))
+ assertFalse(isTopActivityExemptFromDesktopWindowing(mContext,
createFreeformTask(/* displayId */ 0)
.apply {
isTopActivityTransparent = false
numActivities = 1
}))
}
-}
\ No newline at end of file
+
+ @Test
+ fun testIsTopActivityExemptFromDesktopWindowing__topActivityStyleFloating() {
+ assertFalse(isTopActivityExemptFromDesktopWindowing(mContext,
+ createFreeformTask(/* displayId */ 0)
+ .apply {
+ isTopActivityStyleFloating = true
+ }))
+ }
+
+ @Test
+ fun testIsTopActivityExemptFromDesktopWindowing_systemUiTask() {
+ val systemUIPackageName = context.resources.getString(R.string.config_systemUi)
+ val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
+ assertTrue(isTopActivityExemptFromDesktopWindowing(mContext,
+ createFreeformTask(/* displayId */ 0)
+ .apply {
+ baseActivity = baseComponent
+ }))
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index a3b0dc5..bd38d36 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -264,7 +264,7 @@
@Test
fun instantiate_flagOff_doNotAddInitCallback() {
- whenever(DesktopModeStatus.isDesktopModeFlagEnabled()).thenReturn(false)
+ whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(false)
clearInvocations(shellInit)
createController()
@@ -700,19 +700,37 @@
}
@Test
- fun moveToDesktop_topActivityTranslucent_doesNothing() {
- setFlagsRule.enableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+ fun moveToDesktop_topActivityTranslucentWithStyleFloating_taskIsMovedToDesktop() {
val task =
- setUpFullscreenTask().apply {
- isTopActivityTransparent = true
- numActivities = 1
- }
+ setUpFullscreenTask().apply {
+ isTopActivityTransparent = true
+ isTopActivityStyleFloating = true
+ numActivities = 1
+ }
+
+ controller.moveToDesktop(task, transitionSource = UNKNOWN)
+
+ val wct = getLatestEnterDesktopWct()
+ assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+ fun moveToDesktop_topActivityTranslucentWithoutStyleFloating_doesNothing() {
+ val task =
+ setUpFullscreenTask().apply {
+ isTopActivityTransparent = true
+ isTopActivityStyleFloating = false
+ numActivities = 1
+ }
controller.moveToDesktop(task, transitionSource = UNKNOWN)
verifyEnterDesktopWCTNotExecuted()
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
fun moveToDesktop_systemUIActivity_doesNothing() {
val task = setUpFullscreenTask()
@@ -1093,10 +1111,12 @@
val fullscreenTask = createFullscreenTask()
val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
- assertThat(wct?.changes?.get(fullscreenTask.token.asBinder())?.windowingMode)
+
+ assertNotNull(wct, "should handle request")
+ assertThat(wct.changes[fullscreenTask.token.asBinder()]?.windowingMode)
.isEqualTo(WINDOWING_MODE_FREEFORM)
- assertThat(wct!!.hierarchyOps.size).isEqualTo(2)
+ assertThat(wct.hierarchyOps).hasSize(2)
wct.assertReorderAt(1, homeTask, toTop = false)
}
@@ -1372,20 +1392,40 @@
}
@Test
- fun handleRequest_shouldLaunchAsModal_returnSwitchToFullscreenWCT() {
- setFlagsRule.enableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+ fun handleRequest_topActivityTransparentWithStyleFloating_returnSwitchToFreeformWCT() {
+ val freeformTask = setUpFreeformTask()
+ markTaskVisible(freeformTask)
+
val task =
- setUpFreeformTask().apply {
- isTopActivityTransparent = true
- numActivities = 1
- }
+ setUpFullscreenTask().apply {
+ isTopActivityTransparent = true
+ isTopActivityStyleFloating = true
+ numActivities = 1
+ }
val result = controller.handleRequest(Binder(), createTransition(task))
assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
- .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+ fun handleRequest_topActivityTransparentWithoutStyleFloating_returnSwitchToFullscreenWCT() {
+ val task =
+ setUpFreeformTask().apply {
+ isTopActivityTransparent = true
+ isTopActivityStyleFloating = false
+ numActivities = 1
+ }
+
+ val result = controller.handleRequest(Binder(), createTransition(task))
+ assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
fun handleRequest_systemUIActivity_returnSwitchToFullscreenWCT() {
val task = setUpFreeformTask()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
index 77f917c..4bfa96a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
@@ -24,6 +24,7 @@
import android.view.WindowManager.TRANSIT_OPEN
import android.view.WindowManager.TRANSIT_TO_BACK
import android.window.WindowContainerTransaction
+import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_TASK
import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER
import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito
@@ -205,6 +206,46 @@
}
@Test
+ fun removeLeftoverMinimizedTasks_activeNonMinimizedTasksStillAround_doesNothing() {
+ desktopTaskRepo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 1)
+ desktopTaskRepo.addActiveTask(displayId = DEFAULT_DISPLAY, taskId = 2)
+ desktopTaskRepo.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = 2)
+
+ val wct = WindowContainerTransaction()
+ desktopTasksLimiter.leftoverMinimizedTasksRemover.removeLeftoverMinimizedTasks(
+ DEFAULT_DISPLAY, wct)
+
+ assertThat(wct.isEmpty).isTrue()
+ }
+
+ @Test
+ fun removeLeftoverMinimizedTasks_noMinimizedTasks_doesNothing() {
+ val wct = WindowContainerTransaction()
+ desktopTasksLimiter.leftoverMinimizedTasksRemover.removeLeftoverMinimizedTasks(
+ DEFAULT_DISPLAY, wct)
+
+ assertThat(wct.isEmpty).isTrue()
+ }
+
+ @Test
+ fun removeLeftoverMinimizedTasks_onlyMinimizedTasksLeft_removesAllMinimizedTasks() {
+ val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
+ desktopTaskRepo.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task1.taskId)
+ desktopTaskRepo.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+
+ val wct = WindowContainerTransaction()
+ desktopTasksLimiter.leftoverMinimizedTasksRemover.removeLeftoverMinimizedTasks(
+ DEFAULT_DISPLAY, wct)
+
+ assertThat(wct.hierarchyOps).hasSize(2)
+ assertThat(wct.hierarchyOps[0].type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK)
+ assertThat(wct.hierarchyOps[0].container).isEqualTo(task1.token.asBinder())
+ assertThat(wct.hierarchyOps[1].type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK)
+ assertThat(wct.hierarchyOps[1].container).isEqualTo(task2.token.asBinder())
+ }
+
+ @Test
fun addAndGetMinimizeTaskChangesIfNeeded_tasksWithinLimit_noTaskMinimized() {
val taskLimit = desktopTasksLimiter.getMaxTaskLimit()
(1..<taskLimit).forEach { _ -> setUpFreeformTask() }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt
new file mode 100644
index 0000000..17983b2
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlagsTest.kt
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.shared.desktopmode
+
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+import android.provider.Settings
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
+import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE
+import com.android.window.flags.Flags.FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.shared.DesktopModeStatus
+import com.android.wm.shell.shared.desktopmode.DesktopModeFlags.DESKTOP_WINDOWING_MODE
+import com.android.wm.shell.shared.desktopmode.DesktopModeFlags.ToggleOverride.OVERRIDE_OFF
+import com.android.wm.shell.shared.desktopmode.DesktopModeFlags.ToggleOverride.OVERRIDE_ON
+import com.android.wm.shell.shared.desktopmode.DesktopModeFlags.ToggleOverride.OVERRIDE_UNSET
+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.kotlin.whenever
+import org.mockito.quality.Strictness
+
+/**
+ * Test class for [DesktopModeFlags]
+ *
+ * Usage: atest WMShellUnitTests:DesktopModeFlagsTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DesktopModeFlagsTest : ShellTestCase() {
+
+ @JvmField @Rule val setFlagsRule = SetFlagsRule()
+
+ @Before
+ fun setUp() {
+ resetCache()
+ }
+
+ // TODO(b/348193756): Add tests
+ // isEnabled_flagNotOverridable_overrideOff_featureFlagOn_returnsTrue and
+ // isEnabled_flagNotOverridable_overrideOn_featureFlagOff_returnsFalse after adding non
+ // overridable flags to DesktopModeFlags.
+
+ @Test
+ @DisableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
+ @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ fun isEnabled_devOptionFlagDisabled_overrideOff_featureFlagOn_returnsTrue() {
+ setOverride(OVERRIDE_OFF.setting)
+
+ // In absence of dev options, follow flag
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
+ }
+
+ @Test
+ @DisableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ fun isEnabled_devOptionFlagDisabled_overrideOn_featureFlagOff_returnsFalse() {
+ setOverride(OVERRIDE_ON.setting)
+
+ // In absence of dev options, follow flag
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ fun isEnabled_overrideUnset_featureFlagOn_returnsTrue() {
+ setOverride(OVERRIDE_UNSET.setting)
+
+ // For overridableFlag, for unset overrides, follow flag
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
+ @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ fun isEnabled_overrideUnset_featureFlagOff_returnsFalse() {
+ setOverride(OVERRIDE_UNSET.setting)
+
+ // For overridableFlag, for unset overrides, follow flag
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ fun isEnabled_noOverride_featureFlagOn_returnsTrue() {
+ setOverride(null)
+
+ // For overridableFlag, in absence of overrides, follow flag
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
+ @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ fun isEnabled_noOverride_featureFlagOff_returnsFalse() {
+ setOverride(null)
+
+ // For overridableFlag, in absence of overrides, follow flag
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ fun isEnabled_unrecognizableOverride_featureFlagOn_returnsTrue() {
+ setOverride(-2)
+
+ // For overridableFlag, for recognizable overrides, follow flag
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
+ @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ fun isEnabled_unrecognizableOverride_featureFlagOff_returnsFalse() {
+ setOverride(-2)
+
+ // For overridableFlag, for recognizable overrides, follow flag
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ fun isEnabled_overrideOff_featureFlagOn_returnsFalse() {
+ setOverride(OVERRIDE_OFF.setting)
+
+ // For overridableFlag, follow override if they exist
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
+ @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ fun isEnabled_overrideOn_featureFlagOff_returnsTrue() {
+ setOverride(OVERRIDE_ON.setting)
+
+ // For overridableFlag, follow override if they exist
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ fun isEnabled_overrideOffThenOn_featureFlagOn_returnsFalseAndFalse() {
+ setOverride(OVERRIDE_OFF.setting)
+
+ // For overridableFlag, follow override if they exist
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
+
+ setOverride(OVERRIDE_ON.setting)
+
+ // Keep overrides constant through the process
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
+ @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ fun isEnabled_overrideOnThenOff_featureFlagOff_returnsTrueAndTrue() {
+ setOverride(OVERRIDE_ON.setting)
+
+ // For overridableFlag, follow override if they exist
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
+
+ setOverride(OVERRIDE_OFF.setting)
+
+ // Keep overrides constant through the process
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ fun isEnabled_noOverride_featureFlagOnThenOff_returnsTrueAndFalse() {
+ setOverride(null)
+ // For overridableFlag, in absence of overrides, follow flag
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
+
+ val mockitoSession: StaticMockitoSession =
+ ExtendedMockito.mockitoSession()
+ .strictness(Strictness.LENIENT)
+ .spyStatic(DesktopModeStatus::class.java)
+ .startMocking()
+ try {
+ // No caching of flags
+ whenever(DesktopModeStatus.isDesktopModeFlagEnabled()).thenReturn(false)
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
+ } finally {
+ mockitoSession.finishMocking()
+ }
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
+ @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ fun isEnabled_noSystemProperty_overrideOn_featureFlagOff_returnsTrueAndStoresPropertyOn() {
+ System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)
+ setOverride(OVERRIDE_ON.setting)
+
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
+ // Store System Property if not present
+ assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
+ .isEqualTo(OVERRIDE_ON.setting.toString())
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ fun isEnabled_noSystemProperty_overrideUnset_featureFlagOn_returnsTrueAndStoresPropertyUnset() {
+ System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)
+ setOverride(OVERRIDE_UNSET.setting)
+
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
+ // Store System Property if not present
+ assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
+ .isEqualTo(OVERRIDE_UNSET.setting.toString())
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
+ @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ fun isEnabled_noSystemProperty_overrideUnset_featureFlagOff_returnsFalseAndStoresPropertyUnset() {
+ System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)
+ setOverride(OVERRIDE_UNSET.setting)
+
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
+ // Store System Property if not present
+ assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
+ .isEqualTo(OVERRIDE_UNSET.setting.toString())
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ @Suppress("ktlint:standard:max-line-length")
+ fun isEnabled_systemPropertyNotInteger_overrideOff_featureFlagOn_returnsFalseAndStoresPropertyOff() {
+ System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, "abc")
+ setOverride(OVERRIDE_OFF.setting)
+
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
+ // Store System Property if currently invalid
+ assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
+ .isEqualTo(OVERRIDE_OFF.setting.toString())
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ @Suppress("ktlint:standard:max-line-length")
+ fun isEnabled_systemPropertyInvalidInteger_overrideOff_featureFlagOn_returnsFalseAndStoresPropertyOff() {
+ System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, "-2")
+ setOverride(OVERRIDE_OFF.setting)
+
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
+ // Store System Property if currently invalid
+ assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
+ .isEqualTo(OVERRIDE_OFF.setting.toString())
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ fun isEnabled_systemPropertyOff_overrideOn_featureFlagOn_returnsFalseAndDoesNotUpdateProperty() {
+ System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, OVERRIDE_OFF.setting.toString())
+ setOverride(OVERRIDE_ON.setting)
+
+ // Have a consistent override until reboot
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isFalse()
+ assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
+ .isEqualTo(OVERRIDE_OFF.setting.toString())
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
+ @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ fun isEnabled_systemPropertyOn_overrideOff_featureFlagOff_returnsTrueAndDoesNotUpdateProperty() {
+ System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, OVERRIDE_ON.setting.toString())
+ setOverride(OVERRIDE_OFF.setting)
+
+ // Have a consistent override until reboot
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
+ assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
+ .isEqualTo(OVERRIDE_ON.setting.toString())
+ }
+
+ @Test
+ @EnableFlags(FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION, FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ @Suppress("ktlint:standard:max-line-length")
+ fun isEnabled_systemPropertyUnset_overrideOff_featureFlagOn_returnsTrueAndDoesNotUpdateProperty() {
+ System.setProperty(SYSTEM_PROPERTY_OVERRIDE_KEY, OVERRIDE_UNSET.setting.toString())
+ setOverride(OVERRIDE_OFF.setting)
+
+ // Have a consistent override until reboot
+ assertThat(DESKTOP_WINDOWING_MODE.isEnabled(mContext)).isTrue()
+ assertThat(System.getProperty(SYSTEM_PROPERTY_OVERRIDE_KEY))
+ .isEqualTo(OVERRIDE_UNSET.setting.toString())
+ }
+
+ private fun setOverride(setting: Int?) {
+ val contentResolver = mContext.contentResolver
+ val key = Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES
+ if (setting == null) {
+ Settings.Global.putString(contentResolver, key, null)
+ } else {
+ Settings.Global.putInt(contentResolver, key, setting)
+ }
+ }
+
+ private fun resetCache() {
+ val cachedToggleOverride =
+ DESKTOP_WINDOWING_MODE::class.java.getDeclaredField("cachedToggleOverride")
+ cachedToggleOverride.isAccessible = true
+ cachedToggleOverride.set(DESKTOP_WINDOWING_MODE, null)
+
+ // Clear override cache stored in System property
+ System.clearProperty(SYSTEM_PROPERTY_OVERRIDE_KEY)
+ }
+
+ private companion object {
+ const val SYSTEM_PROPERTY_OVERRIDE_KEY = "sys.wmshell.desktopmode.dev_toggle_override"
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/OWNERS b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/OWNERS
new file mode 100644
index 0000000..2fabd4a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/OWNERS
@@ -0,0 +1 @@
+file:/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 8331d59..409b877 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -1191,8 +1191,7 @@
mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class));
final RecentsTransitionHandler recentsHandler =
new RecentsTransitionHandler(shellInit, transitions,
- mock(RecentTasksController.class), mock(HomeTransitionObserver.class),
- () -> mock(SurfaceControl.Transaction.class));
+ mock(RecentTasksController.class), mock(HomeTransitionObserver.class));
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
shellInit.init();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index 0ec6713..0bf5a67 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -348,10 +348,35 @@
}
@Test
- fun testDecorationIsNotCreatedForTopTranslucentActivities() {
- setFlagsRule.enableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+ fun testDecorationIsCreatedForTopTranslucentActivitiesWithStyleFloating() {
+ val mockitoSession: StaticMockitoSession = mockitoSession()
+ .strictness(Strictness.LENIENT)
+ .spyStatic(DesktopModeStatus::class.java)
+ .startMocking()
+ try {
+ val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true).apply {
+ isTopActivityTransparent = true
+ isTopActivityStyleFloating = true
+ numActivities = 1
+ }
+ doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+ setUpMockDecorationsForTasks(task)
+
+ onTaskOpening(task)
+ verify(mockDesktopModeWindowDecorFactory)
+ .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+ } finally {
+ mockitoSession.finishMocking()
+ }
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
+ fun testDecorationIsNotCreatedForTopTranslucentActivitiesWithoutStyleFloating() {
val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true).apply {
isTopActivityTransparent = true
+ isTopActivityStyleFloating = false
numActivities = 1
}
onTaskOpening(task)
@@ -361,6 +386,7 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
fun testDecorationIsNotCreatedForSystemUIActivities() {
val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true)
diff --git a/libs/androidfw/StringPool.cpp b/libs/androidfw/StringPool.cpp
index 1cb8df3..ad445c0 100644
--- a/libs/androidfw/StringPool.cpp
+++ b/libs/androidfw/StringPool.cpp
@@ -132,7 +132,7 @@
auto rhs_iter = rhs.entry_->spans.begin();
for (const Span& span : entry_->spans) {
- const Span& rhs_span = *rhs_iter;
+ const Span& rhs_span = *rhs_iter++;
if (span.first_char != rhs_span.first_char || span.last_char != rhs_span.last_char ||
span.name != rhs_span.name) {
return false;
diff --git a/packages/SettingsLib/ActionButtonsPreference/Android.bp b/packages/SettingsLib/ActionButtonsPreference/Android.bp
index c36b82d..71ecb4c 100644
--- a/packages/SettingsLib/ActionButtonsPreference/Android.bp
+++ b/packages/SettingsLib/ActionButtonsPreference/Android.bp
@@ -19,7 +19,6 @@
static_libs: [
"androidx.preference_preference",
- "SettingsLibUtils",
],
sdk_version: "system_current",
diff --git a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
index 3e65d94..5dc11cf 100644
--- a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
+++ b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
@@ -20,6 +20,7 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
@@ -31,12 +32,11 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
-import com.android.settingslib.utils.BuildCompatUtils;
+import com.android.settingslib.widget.preference.actionbuttons.R;
+
import java.util.ArrayList;
import java.util.List;
-import com.android.settingslib.widget.preference.actionbuttons.R;
-
/**
* This preference provides a four buttons layout with Settings style.
* It looks like below
@@ -56,7 +56,7 @@
public class ActionButtonsPreference extends Preference {
private static final String TAG = "ActionButtonPreference";
- private static final boolean mIsAtLeastS = BuildCompatUtils.isAtLeastS();
+ private static final boolean mIsAtLeastS = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
private static final int SINGLE_BUTTON_STYLE = 1;
private static final int TWO_BUTTONS_STYLE = 2;
private static final int THREE_BUTTONS_STYLE = 3;
diff --git a/packages/SettingsLib/ActivityEmbedding/Android.bp b/packages/SettingsLib/ActivityEmbedding/Android.bp
index 838a9e5..5561002 100644
--- a/packages/SettingsLib/ActivityEmbedding/Android.bp
+++ b/packages/SettingsLib/ActivityEmbedding/Android.bp
@@ -20,7 +20,6 @@
"androidx.annotation_annotation",
"androidx.core_core",
"androidx.window_window",
- "SettingsLibUtils",
],
sdk_version: "system_current",
min_sdk_version: "21",
diff --git a/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java b/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java
index f89be9f..67aa8f4 100644
--- a/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java
+++ b/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java
@@ -20,13 +20,11 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.os.Build;
import android.util.Log;
-import androidx.core.os.BuildCompat;
import androidx.window.embedding.ActivityEmbeddingController;
-import com.android.settingslib.utils.BuildCompatUtils;
-
/**
* An util class collecting all common methods for the embedding activity features.
*/
@@ -70,7 +68,7 @@
* enabled (unsupported devices).
*/
private static ComponentName getEmbeddingActivityComponent(Context context) {
- if (!BuildCompatUtils.isAtLeastSV2()) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S_V2) {
return null;
}
final Intent intent = new Intent(ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY);
@@ -95,7 +93,7 @@
* Settings app
*/
public static boolean shouldHideNavigateUpButton(Activity activity, boolean isSecondLayerPage) {
- if (!BuildCompat.isAtLeastT()) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
return false;
}
diff --git a/packages/SettingsLib/BannerMessagePreference/Android.bp b/packages/SettingsLib/BannerMessagePreference/Android.bp
index 07290de..3f671b9 100644
--- a/packages/SettingsLib/BannerMessagePreference/Android.bp
+++ b/packages/SettingsLib/BannerMessagePreference/Android.bp
@@ -20,7 +20,6 @@
static_libs: [
"androidx.preference_preference",
"SettingsLibSettingsTheme",
- "SettingsLibUtils",
],
sdk_version: "system_current",
diff --git a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
index 33775a6..6cd777e 100644
--- a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
+++ b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
@@ -38,7 +38,6 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
-import com.android.settingslib.utils.BuildCompatUtils;
import com.android.settingslib.widget.preference.banner.R;
/**
* Banner message is a banner displaying important information (permission request, page error etc),
@@ -84,7 +83,7 @@
}
private static final String TAG = "BannerPreference";
- private static final boolean IS_AT_LEAST_S = BuildCompatUtils.isAtLeastS();
+ private static final boolean IS_AT_LEAST_S = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
private final BannerMessagePreference.ButtonInfo mPositiveButtonInfo =
new BannerMessagePreference.ButtonInfo();
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp b/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp
index 87ec0b8..4834039 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp
@@ -22,7 +22,6 @@
"androidx.core_core",
"com.google.android.material_material",
"SettingsLibSettingsTransition",
- "SettingsLibUtils",
"SettingsLibSettingsTheme",
],
sdk_version: "system_current",
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/BasePreferencesFragment.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/BasePreferencesFragment.java
index 8ebbac3..b252e5f 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/BasePreferencesFragment.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/BasePreferencesFragment.java
@@ -16,11 +16,11 @@
package com.android.settingslib.collapsingtoolbar;
+import android.os.Build;
+
import androidx.fragment.app.FragmentActivity;
import androidx.preference.PreferenceFragmentCompat;
-import com.android.settingslib.utils.BuildCompatUtils;
-
import com.google.android.material.appbar.AppBarLayout;
/**
@@ -58,7 +58,7 @@
if (activity != null) {
activity.setTitle(getTitle());
- if (BuildCompatUtils.isAtLeastS()) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
AppBarLayout appBarLayout = (AppBarLayout) activity.findViewById(R.id.app_bar);
if (appBarLayout != null) {
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java
index 04c44e6..8b27626 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java
@@ -17,6 +17,7 @@
package com.android.settingslib.collapsingtoolbar;
import android.app.ActionBar;
+import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@@ -26,8 +27,6 @@
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
-import com.android.settingslib.utils.BuildCompatUtils;
-
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.CollapsingToolbarLayout;
import com.google.android.material.color.DynamicColors;
@@ -66,12 +65,12 @@
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- if (BuildCompatUtils.isAtLeastS()) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
DynamicColors.applyToActivityIfAvailable(this);
}
setTheme(com.android.settingslib.widget.theme.R.style.Theme_SubSettingsBase);
- if (mCustomizeLayoutResId > 0 && !BuildCompatUtils.isAtLeastS()) {
+ if (mCustomizeLayoutResId > 0 && Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
super.setContentView(mCustomizeLayoutResId);
return;
}
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
index 143101f..86ce2ab 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
@@ -18,6 +18,7 @@
import android.app.ActionBar;
import android.content.pm.PackageManager;
+import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@@ -27,8 +28,6 @@
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;
-import com.android.settingslib.utils.BuildCompatUtils;
-
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.CollapsingToolbarLayout;
@@ -60,7 +59,8 @@
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// for backward compatibility on R devices or wearable devices due to small device size.
- if (mCustomizeLayoutResId > 0 && (!BuildCompatUtils.isAtLeastS() || isWatch())) {
+ if (mCustomizeLayoutResId > 0 && (Build.VERSION.SDK_INT < Build.VERSION_CODES.S
+ || isWatch())) {
super.setContentView(mCustomizeLayoutResId);
return;
}
diff --git a/packages/SettingsLib/ProfileSelector/src/com/android/settingslib/widget/ProfileSelectFragment.java b/packages/SettingsLib/ProfileSelector/src/com/android/settingslib/widget/ProfileSelectFragment.java
index c52386b..7be4482 100644
--- a/packages/SettingsLib/ProfileSelector/src/com/android/settingslib/widget/ProfileSelectFragment.java
+++ b/packages/SettingsLib/ProfileSelector/src/com/android/settingslib/widget/ProfileSelectFragment.java
@@ -30,7 +30,6 @@
import android.view.View;
import android.view.ViewGroup;
-import androidx.core.os.BuildCompat;
import androidx.fragment.app.Fragment;
import androidx.viewpager2.widget.ViewPager2;
@@ -226,7 +225,8 @@
// to be here only for this API level - when then private profile was introduced.
@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
private boolean shouldShowPrivateProfileIfItsOne(UserHandle userHandle) {
- if (!BuildCompat.isAtLeastV() || !android.os.Flags.allowPrivateProfile()) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM
+ || !android.os.Flags.allowPrivateProfile()) {
return false;
}
try {
diff --git a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/BuildCompatUtils.java b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/BuildCompatUtils.java
deleted file mode 100644
index bf3651b..0000000
--- a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/BuildCompatUtils.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.utils;
-
-import android.os.Build;
-
-import androidx.annotation.ChecksSdkIntAtLeast;
-
-/**
- * An util class to check whether the current OS version is higher or equal to sdk version of
- * device.
- */
-public final class BuildCompatUtils {
-
- /**
- * Implementation of BuildCompat.isAtLeastS() suitable for use in Settings
- *
- * @return Whether the current OS version is higher or equal to S.
- */
- @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.S)
- public static boolean isAtLeastS() {
- return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
- }
-
- /**
- * Implementation of BuildCompat.isAtLeastS() suitable for use in Settings
- *
- * @return Whether the current OS version is higher or equal to Sv2.
- */
- @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.S_V2)
- public static boolean isAtLeastSV2() {
- return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S_V2;
- }
-
- /**
- * Implementation of BuildCompat.isAtLeastT() suitable for use in Settings
- *
- * @return Whether the current OS version is higher or equal to T.
- */
- @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.TIRAMISU)
- public static boolean isAtLeastT() {
- return Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU;
- }
-
- private BuildCompatUtils() {}
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
index 734b92c..6ca9279 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -38,8 +38,6 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
-import com.android.settingslib.utils.BuildCompatUtils;
-
/**
* Helper class for managing settings preferences that can be disabled
* by device admins via user restrictions.
@@ -120,9 +118,10 @@
if (mDisabledSummary) {
final TextView summaryView = (TextView) holder.findViewById(android.R.id.summary);
if (summaryView != null) {
- final CharSequence disabledText = BuildCompatUtils.isAtLeastT()
- ? getDisabledByAdminUpdatableString()
- : mContext.getString(R.string.disabled_by_admin_summary_text);
+ final CharSequence disabledText =
+ (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
+ ? getDisabledByAdminUpdatableString()
+ : mContext.getString(R.string.disabled_by_admin_summary_text);
if (mDisabledByAdmin) {
summaryView.setText(disabledText);
} else if (mDisabledByEcm) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
index 45754eb..fffbb54 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -25,6 +25,7 @@
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.res.TypedArray;
+import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
import android.util.AttributeSet;
@@ -40,8 +41,6 @@
import androidx.preference.PreferenceViewHolder;
import androidx.preference.SwitchPreferenceCompat;
-import com.android.settingslib.utils.BuildCompatUtils;
-
/**
* Version of SwitchPreferenceCompat that can be disabled by a device admin
* using a user restriction.
@@ -164,7 +163,7 @@
private static String getUpdatableEnterpriseString(
Context context, String updatableStringId, int resId) {
- if (!BuildCompatUtils.isAtLeastT()) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
return context.getString(resId);
}
return context.getSystemService(DevicePolicyManager.class).getResources().getString(
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index c2506d3..b02b0c4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -66,7 +66,6 @@
import com.android.settingslib.drawable.UserIconDrawable;
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.settingslib.fuelgauge.BatteryUtils;
-import com.android.settingslib.utils.BuildCompatUtils;
import java.util.List;
@@ -147,7 +146,7 @@
String name = info != null ? info.name : null;
if (info.isManagedProfile()) {
// We use predefined values for managed profiles
- return BuildCompatUtils.isAtLeastT()
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
? getUpdatableManagedUserTitle(context)
: context.getString(R.string.managed_user_title);
} else if (info.isGuest()) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 721e7b9..53441c0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -24,6 +24,7 @@
import android.net.Uri;
import android.provider.DeviceConfig;
import android.provider.MediaStore;
+import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -808,7 +809,8 @@
* <p>If CachedBluetoothDevice#getGroupId is invalid, fetch group id from
* LeAudioProfile#getGroupId.
*/
- public static int getGroupId(@NonNull CachedBluetoothDevice cachedDevice) {
+ public static int getGroupId(@Nullable CachedBluetoothDevice cachedDevice) {
+ if (cachedDevice == null) return BluetoothCsipSetCoordinator.GROUP_ID_INVALID;
int groupId = cachedDevice.getGroupId();
String anonymizedAddress = cachedDevice.getDevice().getAnonymizedAddress();
if (groupId != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
@@ -824,4 +826,44 @@
Log.d(TAG, "getGroupId return invalid id for device: " + anonymizedAddress);
return BluetoothCsipSetCoordinator.GROUP_ID_INVALID;
}
+
+ /** Get primary device Uri in broadcast. */
+ @NonNull
+ public static String getPrimaryGroupIdUriForBroadcast() {
+ return "bluetooth_le_broadcast_fallback_active_group_id";
+ }
+
+ /** Get primary device group id in broadcast. */
+ @WorkerThread
+ public static int getPrimaryGroupIdForBroadcast(@NonNull Context context) {
+ return Settings.Secure.getInt(
+ context.getContentResolver(),
+ getPrimaryGroupIdUriForBroadcast(),
+ BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
+ }
+
+ /** Get secondary {@link CachedBluetoothDevice} in broadcast. */
+ @Nullable
+ @WorkerThread
+ public static CachedBluetoothDevice getSecondaryDeviceForBroadcast(
+ @NonNull Context context, @Nullable LocalBluetoothManager localBtManager) {
+ if (localBtManager == null) return null;
+ int primaryGroupId = getPrimaryGroupIdForBroadcast(context);
+ if (primaryGroupId == BluetoothCsipSetCoordinator.GROUP_ID_INVALID) return null;
+ LocalBluetoothLeBroadcastAssistant assistant =
+ localBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
+ CachedBluetoothDeviceManager deviceManager = localBtManager.getCachedDeviceManager();
+ List<BluetoothDevice> devices = assistant.getAllConnectedDevices();
+ for (BluetoothDevice device : devices) {
+ CachedBluetoothDevice cachedDevice = deviceManager.findDevice(device);
+ if (hasConnectedBroadcastSource(cachedDevice, localBtManager)) {
+ int groupId = getGroupId(cachedDevice);
+ if (groupId != BluetoothCsipSetCoordinator.GROUP_ID_INVALID
+ && groupId != primaryGroupId) {
+ return cachedDevice;
+ }
+ }
+ }
+ return null;
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
index ea65ade..84afb9f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
@@ -25,7 +25,6 @@
import androidx.annotation.EmptySuper;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
-import androidx.core.os.BuildCompat;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.Preference;
import androidx.preference.PreferenceGroup;
@@ -141,7 +140,7 @@
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
protected void replaceEnterpriseStringTitle(PreferenceScreen screen,
String preferenceKey, String overrideKey, int resource) {
- if (!BuildCompat.isAtLeastT() || mDevicePolicyManager == null) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU || mDevicePolicyManager == null) {
return;
}
@@ -159,7 +158,7 @@
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
protected void replaceEnterpriseStringSummary(
PreferenceScreen screen, String preferenceKey, String overrideKey, int resource) {
- if (!BuildCompat.isAtLeastT() || mDevicePolicyManager == null) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU || mDevicePolicyManager == null) {
return;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
index f07daa3..243403e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
@@ -48,8 +48,6 @@
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
-import com.android.settingslib.utils.BuildCompatUtils;
-
/**
* Converts the user avatar icon to a circularly clipped one with an optional badge and frame
*/
@@ -88,7 +86,7 @@
* @return drawable containing just the badge
*/
public static Drawable getManagedUserDrawable(Context context) {
- if (BuildCompatUtils.isAtLeastT()) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
return getUpdatableManagedUserDrawable(context);
} else {
return getDrawableForDisplayDensity(
@@ -227,7 +225,7 @@
}
private static Drawable getManagementBadge(Context context) {
- if (BuildCompatUtils.isAtLeastT()) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
return getUpdatableManagementBadge(context);
} else {
return getDrawableForDisplayDensity(
diff --git a/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/ZenModeRepository.kt b/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/ZenModeRepository.kt
index 4d25237..2952764 100644
--- a/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/ZenModeRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/ZenModeRepository.kt
@@ -73,12 +73,12 @@
awaitClose { context.unregisterReceiver(receiver) }
}
- .apply {
+ .let {
if (Flags.volumePanelBroadcastFix()) {
- flowOn(backgroundCoroutineContext)
- stateIn(scope, SharingStarted.WhileSubscribed(), null)
+ it.flowOn(backgroundCoroutineContext)
+ .stateIn(scope, SharingStarted.WhileSubscribed(), null)
} else {
- shareIn(
+ it.shareIn(
started = SharingStarted.WhileSubscribed(),
scope = scope,
)
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
index a638df5..28bf348 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
@@ -22,11 +22,13 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.content.Context;
@@ -36,10 +38,13 @@
import android.media.AudioManager;
import android.net.Uri;
import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
import android.util.Pair;
import com.android.settingslib.widget.AdaptiveIcon;
+import com.google.common.collect.ImmutableList;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -562,4 +567,77 @@
assertThat(BluetoothUtils.isAvailableHearingDevice(mCachedBluetoothDevice)).isEqualTo(true);
}
+
+ @Test
+ public void getGroupId_getCsipProfileId() {
+ when(mCachedBluetoothDevice.getGroupId()).thenReturn(1);
+
+ assertThat(BluetoothUtils.getGroupId(mCachedBluetoothDevice)).isEqualTo(1);
+ }
+
+ @Test
+ public void getGroupId_getLeAudioProfileId() {
+ when(mCachedBluetoothDevice.getGroupId())
+ .thenReturn(BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+ LeAudioProfile leAudio = mock(LeAudioProfile.class);
+ when(leAudio.getGroupId(mBluetoothDevice)).thenReturn(1);
+ when(mCachedBluetoothDevice.getProfiles()).thenReturn(ImmutableList.of(leAudio));
+
+ assertThat(BluetoothUtils.getGroupId(mCachedBluetoothDevice)).isEqualTo(1);
+ }
+
+ @Test
+ public void getSecondaryDeviceForBroadcast_errorState_returnNull() {
+ assertThat(BluetoothUtils.getSecondaryDeviceForBroadcast(mContext, mLocalBluetoothManager))
+ .isNull();
+ }
+
+ @Test
+ public void getSecondaryDeviceForBroadcast_noSecondary_returnNull() {
+ Settings.Secure.putInt(
+ mContext.getContentResolver(),
+ BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
+ 1);
+ CachedBluetoothDeviceManager deviceManager = mock(CachedBluetoothDeviceManager.class);
+ when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(deviceManager);
+ when(deviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice);
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+ when(mCachedBluetoothDevice.getGroupId()).thenReturn(1);
+ BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class);
+ when(mAssistant.getAllSources(mBluetoothDevice)).thenReturn(ImmutableList.of(state));
+ when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mBluetoothDevice));
+
+ assertThat(BluetoothUtils.getSecondaryDeviceForBroadcast(mContext, mLocalBluetoothManager))
+ .isNull();
+ }
+
+ @Test
+ public void getSecondaryDeviceForBroadcast_returnCorrectDevice() {
+ Settings.Secure.putInt(
+ mContext.getContentResolver(),
+ BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
+ 1);
+ CachedBluetoothDeviceManager deviceManager = mock(CachedBluetoothDeviceManager.class);
+ when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(deviceManager);
+ CachedBluetoothDevice cachedBluetoothDevice = mock(CachedBluetoothDevice.class);
+ BluetoothDevice bluetoothDevice = mock(BluetoothDevice.class);
+ when(cachedBluetoothDevice.getDevice()).thenReturn(bluetoothDevice);
+ when(cachedBluetoothDevice.getGroupId()).thenReturn(1);
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+ when(mCachedBluetoothDevice.getGroupId()).thenReturn(2);
+ when(deviceManager.findDevice(bluetoothDevice)).thenReturn(cachedBluetoothDevice);
+ when(deviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice);
+ BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class);
+ List<Long> bisSyncState = new ArrayList<>();
+ bisSyncState.add(1L);
+ when(state.getBisSyncState()).thenReturn(bisSyncState);
+ when(mAssistant.getAllSources(any(BluetoothDevice.class)))
+ .thenReturn(ImmutableList.of(state));
+ when(mAssistant.getAllConnectedDevices())
+ .thenReturn(ImmutableList.of(mBluetoothDevice, bluetoothDevice));
+
+ assertThat(BluetoothUtils.getSecondaryDeviceForBroadcast(mContext, mLocalBluetoothManager))
+ .isEqualTo(mCachedBluetoothDevice);
+ }
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index 00fb7a1..2cdd0ae 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -80,6 +80,7 @@
Settings.System.SIP_RECEIVE_CALLS,
Settings.System.POINTER_SPEED,
Settings.System.POINTER_FILL_STYLE,
+ Settings.System.POINTER_STROKE_STYLE,
Settings.System.POINTER_SCALE,
Settings.System.VIBRATE_ON,
Settings.System.VIBRATE_WHEN_RINGING,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 4235bc4..7b927d7 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -30,6 +30,8 @@
import static android.view.PointerIcon.LARGE_POINTER_SCALE;
import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_BEGIN;
import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_END;
+import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_STROKE_BEGIN;
+import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_STROKE_END;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
@@ -213,6 +215,9 @@
VALIDATORS.put(System.POINTER_FILL_STYLE,
new InclusiveIntegerRangeValidator(POINTER_ICON_VECTOR_STYLE_FILL_BEGIN,
POINTER_ICON_VECTOR_STYLE_FILL_END));
+ VALIDATORS.put(System.POINTER_STROKE_STYLE,
+ new InclusiveIntegerRangeValidator(POINTER_ICON_VECTOR_STYLE_STROKE_BEGIN,
+ POINTER_ICON_VECTOR_STYLE_STROKE_END));
VALIDATORS.put(System.POINTER_SCALE,
new InclusiveFloatRangeValidator(DEFAULT_POINTER_SCALE, LARGE_POINTER_SCALE));
VALIDATORS.put(System.TOUCHPAD_POINTER_SPEED, new InclusiveIntegerRangeValidator(-7, 7));
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 384cb7e..cd37ad1 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -2916,6 +2916,9 @@
Settings.System.POINTER_FILL_STYLE,
SystemSettingsProto.Pointer.POINTER_FILL_STYLE);
dumpSetting(s, p,
+ Settings.System.POINTER_STROKE_STYLE,
+ SystemSettingsProto.Pointer.POINTER_STROKE_STYLE);
+ dumpSetting(s, p,
Settings.System.POINTER_SCALE,
SystemSettingsProto.Pointer.POINTER_SCALE);
p.end(pointerToken);
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index b5776e2..469b9ce 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -23,7 +23,10 @@
// See: http://go/android-license-faq
license {
name: "frameworks_base_packages_SystemUI_license",
- visibility: [":__subpackages__"],
+ visibility: [
+ ":__subpackages__",
+ "//development/samples/SceneTransitionLayoutDemo:__subpackages__",
+ ],
license_kinds: [
"SPDX-license-identifier-Apache-2.0",
],
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 18fbf77..6bbac45 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1052,13 +1052,6 @@
}
flag {
- name: "glanceable_hub_gesture_handle"
- namespace: "systemui"
- description: "Shows a vertical bar at the right edge to indicate the user can swipe to open the glanceable hub"
- bug: "339667383"
-}
-
-flag {
name: "glanceable_hub_allow_keyguard_when_dreaming"
namespace: "systemui"
description: "Allows users to exit dream to keyguard with glanceable hub enabled"
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index bb76c1d..cc4e775 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -9,21 +9,14 @@
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.isSystemInDarkTheme
-import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.drawBehind
@@ -48,7 +41,6 @@
import com.android.compose.animation.scene.observableTransitionState
import com.android.compose.animation.scene.transitions
import com.android.compose.theme.LocalAndroidColorScheme
-import com.android.systemui.Flags
import com.android.systemui.communal.shared.model.CommunalBackgroundType
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalTransitionKeys
@@ -156,8 +148,6 @@
val currentSceneKey: SceneKey by
viewModel.currentScene.collectAsStateWithLifecycle(CommunalScenes.Blank)
val touchesAllowed by viewModel.touchesAllowed.collectAsStateWithLifecycle()
- val showGestureIndicator by
- viewModel.showGestureIndicator.collectAsStateWithLifecycle(initialValue = false)
val backgroundType by
viewModel.communalBackground.collectAsStateWithLifecycle(
initialValue = CommunalBackgroundType.ANIMATED
@@ -200,19 +190,7 @@
)
) {
// This scene shows nothing only allowing for transitions to the communal scene.
- // TODO(b/339667383): remove this temporary swipe gesture handle
- Row(modifier = Modifier.fillMaxSize(), horizontalArrangement = Arrangement.End) {
- if (showGestureIndicator && Flags.glanceableHubGestureHandle()) {
- Box(
- modifier =
- Modifier.height(220.dp)
- .width(4.dp)
- .align(Alignment.CenterVertically)
- .background(color = Color.White, RoundedCornerShape(4.dp))
- )
- Spacer(modifier = Modifier.width(12.dp))
- }
- }
+ Box(modifier = Modifier.fillMaxSize())
}
scene(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index be51c1a..97ed74f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -119,6 +119,7 @@
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.CustomAccessibilityAction
+import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.customActions
import androidx.compose.ui.semantics.onClick
@@ -871,7 +872,7 @@
Icon(
imageVector = Icons.Outlined.Widgets,
contentDescription = stringResource(R.string.cta_label_to_open_widget_picker),
- modifier = Modifier.size(Dimensions.IconSize),
+ modifier = Modifier.size(Dimensions.IconSize).clearAndSetSemantics {},
)
Spacer(modifier = Modifier.size(6.dp))
Text(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackContentHeight.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackContentHeight.kt
index 9f829cc..22c17f2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackContentHeight.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackContentHeight.kt
@@ -24,7 +24,9 @@
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.node.invalidateMeasurement
import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.dp
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
/**
@@ -32,14 +34,16 @@
* by the legacy Notification stack scroll view in [NotificationScrollView.intrinsicStackHeight].
*
* @param view Notification stack scroll view
- * @param padding extra padding in pixels to be added to the received content height.
+ * @param totalVerticalPadding extra padding to be added to the received stack content height.
*/
-fun Modifier.notificationStackHeight(view: NotificationScrollView, padding: Int = 0) =
- this then StackLayoutElement(view, padding)
+fun Modifier.notificationStackHeight(
+ view: NotificationScrollView,
+ totalVerticalPadding: Dp = 0.dp,
+) = this then StackLayoutElement(view, totalVerticalPadding)
private data class StackLayoutElement(
val view: NotificationScrollView,
- val padding: Int,
+ val padding: Dp,
) : ModifierNodeElement<StackLayoutNode>() {
override fun create(): StackLayoutNode = StackLayoutNode(view, padding)
@@ -53,7 +57,7 @@
}
}
-private class StackLayoutNode(val view: NotificationScrollView, var padding: Int) :
+private class StackLayoutNode(val view: NotificationScrollView, var padding: Dp) :
LayoutModifierNode, Modifier.Node() {
private val stackHeightChangedListener = Runnable { invalidateMeasureIfAttached() }
@@ -72,7 +76,7 @@
measurable: Measurable,
constraints: Constraints
): MeasureResult {
- val contentHeight = padding + view.intrinsicStackHeight
+ val contentHeight = padding.roundToPx() + view.intrinsicStackHeight
val placeable =
measurable.measure(
constraints.copy(minHeight = contentHeight, maxHeight = contentHeight)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index a568824..12ca997 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -289,11 +289,9 @@
viewModel.isCurrentGestureOverscroll.collectAsStateWithLifecycle(false)
val expansionFraction by viewModel.expandFraction.collectAsStateWithLifecycle(0f)
- val navBarHeightPx =
- with(density) {
- WindowInsets.systemBars.asPaddingValues().calculateBottomPadding().toPx().toInt()
- }
- val bottomPaddingPx = if (shouldReserveSpaceForNavBar) navBarHeightPx else 0
+ val topPadding = dimensionResource(id = R.dimen.notification_side_paddings)
+ val navBarHeight = WindowInsets.systemBars.asPaddingValues().calculateBottomPadding()
+ val bottomPadding = if (shouldReserveSpaceForNavBar) navBarHeight else 0.dp
val screenHeight = LocalRawScreenHeight.current
@@ -462,8 +460,12 @@
Modifier.nestedScroll(scrimNestedScrollConnection)
}
.verticalScroll(scrollState)
+ .padding(top = topPadding)
.fillMaxWidth()
- .notificationStackHeight(view = stackScrollView, padding = bottomPaddingPx)
+ .notificationStackHeight(
+ view = stackScrollView,
+ totalVerticalPadding = topPadding + bottomPadding,
+ )
.onSizeChanged { size -> stackHeight.intValue = size.height },
)
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
index e8fdfc8..d95b388 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
@@ -913,6 +913,7 @@
private val topOrLeftBehavior: NestedScrollBehavior,
private val bottomOrRightBehavior: NestedScrollBehavior,
private val isExternalOverscrollGesture: () -> Boolean,
+ private val pointersInfoOwner: PointersInfoOwner,
) {
private val layoutState = layoutImpl.state
private val draggableHandler = layoutImpl.draggableHandler(orientation)
@@ -924,25 +925,12 @@
// moving on to the next scene.
var canChangeScene = false
- val actionUpOrLeft =
- Swipe(
- direction =
- when (orientation) {
- Orientation.Horizontal -> SwipeDirection.Left
- Orientation.Vertical -> SwipeDirection.Up
- },
- pointerCount = 1,
- )
-
- val actionDownOrRight =
- Swipe(
- direction =
- when (orientation) {
- Orientation.Horizontal -> SwipeDirection.Right
- Orientation.Vertical -> SwipeDirection.Down
- },
- pointerCount = 1,
- )
+ var _lastPointersInfo: PointersInfo? = null
+ fun pointersInfo(): PointersInfo {
+ return checkNotNull(_lastPointersInfo) {
+ "PointersInfo should be initialized before the transition begins."
+ }
+ }
fun hasNextScene(amount: Float): Boolean {
val transitionState = layoutState.transitionState
@@ -950,8 +938,30 @@
val fromScene = layoutImpl.scene(scene)
val nextScene =
when {
- amount < 0f -> fromScene.userActions[actionUpOrLeft]
- amount > 0f -> fromScene.userActions[actionDownOrRight]
+ amount < 0f -> {
+ val actionUpOrLeft =
+ Swipe(
+ direction =
+ when (orientation) {
+ Orientation.Horizontal -> SwipeDirection.Left
+ Orientation.Vertical -> SwipeDirection.Up
+ },
+ pointerCount = pointersInfo().pointersDown,
+ )
+ fromScene.userActions[actionUpOrLeft]
+ }
+ amount > 0f -> {
+ val actionDownOrRight =
+ Swipe(
+ direction =
+ when (orientation) {
+ Orientation.Horizontal -> SwipeDirection.Right
+ Orientation.Vertical -> SwipeDirection.Down
+ },
+ pointerCount = pointersInfo().pointersDown,
+ )
+ fromScene.userActions[actionDownOrRight]
+ }
else -> null
}
if (nextScene != null) return true
@@ -985,6 +995,8 @@
return@PriorityNestedScrollConnection false
}
+ _lastPointersInfo = pointersInfoOwner.pointersInfo()
+
// If the current swipe transition is *not* closed to 0f or 1f, then we want the
// scroll events to intercept the current transition to continue the scene
// transition.
@@ -1002,6 +1014,8 @@
val isZeroOffset =
if (isExternalOverscrollGesture()) false else offsetBeforeStart == 0f
+ _lastPointersInfo = pointersInfoOwner.pointersInfo()
+
val canStart =
when (behavior) {
NestedScrollBehavior.DuringTransitionBetweenScenes -> {
@@ -1039,6 +1053,8 @@
// We could start an overscroll animation
canChangeScene = false
+ _lastPointersInfo = pointersInfoOwner.pointersInfo()
+
val canStart = behavior.canStartOnPostFling && hasNextScene(velocityAvailable)
if (canStart) {
isIntercepting = false
@@ -1049,10 +1065,11 @@
canContinueScroll = { true },
canScrollOnFling = false,
onStart = { offsetAvailable ->
+ val pointersInfo = pointersInfo()
dragController =
draggableHandler.onDragStarted(
- pointersDown = 1,
- startedPosition = null,
+ pointersDown = pointersInfo.pointersDown,
+ startedPosition = pointersInfo.startedPosition,
overSlop = if (isIntercepting) 0f else offsetAvailable,
)
},
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
index f40f265..cdcfc84 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
@@ -29,18 +29,21 @@
import androidx.compose.ui.input.pointer.PointerInputChange
import androidx.compose.ui.input.pointer.PointerInputScope
import androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNode
-import androidx.compose.ui.input.pointer.changedToDownIgnoreConsumed
+import androidx.compose.ui.input.pointer.changedToDown
import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed
import androidx.compose.ui.input.pointer.positionChange
import androidx.compose.ui.input.pointer.positionChangeIgnoreConsumed
import androidx.compose.ui.input.pointer.util.VelocityTracker
import androidx.compose.ui.input.pointer.util.addPointerInputChange
import androidx.compose.ui.node.CompositionLocalConsumerModifierNode
+import androidx.compose.ui.node.DelegatableNode
import androidx.compose.ui.node.DelegatingNode
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.node.ObserverModifierNode
import androidx.compose.ui.node.PointerInputModifierNode
+import androidx.compose.ui.node.TraversableNode
import androidx.compose.ui.node.currentValueOf
+import androidx.compose.ui.node.findNearestAncestor
import androidx.compose.ui.node.observeReads
import androidx.compose.ui.platform.LocalViewConfiguration
import androidx.compose.ui.unit.IntSize
@@ -48,11 +51,12 @@
import androidx.compose.ui.util.fastAll
import androidx.compose.ui.util.fastAny
import androidx.compose.ui.util.fastFirstOrNull
-import androidx.compose.ui.util.fastForEach
+import androidx.compose.ui.util.fastSumBy
import kotlin.coroutines.cancellation.CancellationException
import kotlin.math.sign
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.isActive
+import kotlinx.coroutines.launch
/**
* Make an element draggable in the given [orientation].
@@ -112,6 +116,18 @@
}
}
+private val TRAVERSE_KEY = Any()
+
+/** Find the nearest [PointersInfoOwner] ancestor or throw. */
+internal fun DelegatableNode.requireAncestorPointersInfoOwner(): PointersInfoOwner {
+ val ancestorNode =
+ checkNotNull(findNearestAncestor(TRAVERSE_KEY)) {
+ "This should never happen! Couldn't find a MultiPointerDraggableNode. " +
+ "Are we inside an SceneTransitionLayout?"
+ }
+ return ancestorNode as PointersInfoOwner
+}
+
internal class MultiPointerDraggableNode(
orientation: Orientation,
enabled: () -> Boolean,
@@ -120,15 +136,19 @@
(startedPosition: Offset, overSlop: Float, pointersDown: Int) -> DragController,
var swipeDetector: SwipeDetector = DefaultSwipeDetector,
) :
- PointerInputModifierNode,
DelegatingNode(),
+ PointerInputModifierNode,
CompositionLocalConsumerModifierNode,
+ TraversableNode,
+ PointersInfoOwner,
ObserverModifierNode {
private val pointerInputHandler: suspend PointerInputScope.() -> Unit = { pointerInput() }
private val delegate = delegate(SuspendingPointerInputModifierNode(pointerInputHandler))
private val velocityTracker = VelocityTracker()
private var previousEnabled: Boolean = false
+ override val traverseKey: Any = TRAVERSE_KEY
+
var enabled: () -> Boolean = enabled
set(value) {
// Reset the pointer input whenever enabled changed.
@@ -185,12 +205,42 @@
bounds: IntSize
) = delegate.onPointerEvent(pointerEvent, pass, bounds)
+ private var startedPosition: Offset? = null
+ private var pointersDown: Int = 0
+
+ override fun pointersInfo(): PointersInfo {
+ return PointersInfo(
+ startedPosition = startedPosition,
+ // Note: We could have 0 pointers during fling or for other reasons.
+ pointersDown = pointersDown.coerceAtLeast(1),
+ )
+ }
+
private suspend fun PointerInputScope.pointerInput() {
if (!enabled()) {
return
}
coroutineScope {
+ launch {
+ // Intercepts pointer inputs and exposes [PointersInfo], via
+ // [requireAncestorPointersInfoOwner], to our descendants.
+ awaitPointerEventScope {
+ while (isActive) {
+ // During the Initial pass, we receive the event after our ancestors.
+ val pointers = awaitPointerEvent(PointerEventPass.Initial).changes
+
+ pointersDown = pointers.countDown()
+ if (pointersDown == 0) {
+ // There are no more pointers down
+ startedPosition = null
+ } else if (startedPosition == null) {
+ startedPosition = pointers.first().position
+ }
+ }
+ }
+ }
+
awaitPointerEventScope {
while (isActive) {
try {
@@ -314,15 +364,16 @@
}
if (drag != null) {
- // Count the number of pressed pointers.
- val pressed = mutableSetOf<PointerId>()
- currentEvent.changes.fastForEach { change ->
- if (change.pressed) {
- pressed.add(change.id)
- }
- }
-
- val controller = onDragStart(drag.position, overSlop, pressed.size)
+ val controller =
+ onDragStart(
+ // The startedPosition is the starting position when a gesture begins (when the
+ // first pointer touches the screen), not the point where we begin dragging.
+ // For example, this could be different if one of our children intercepts the
+ // gesture first and then we do.
+ requireNotNull(startedPosition),
+ overSlop,
+ pointersDown,
+ )
val successful: Boolean
try {
@@ -364,12 +415,10 @@
fun canBeConsumed(changes: List<PointerInputChange>): Boolean {
// At least one pointer down AND
return changes.fastAny { it.pressed } &&
- // All pointers must be:
+ // All pointers must be either:
changes.fastAll {
- // A) recently pressed: even if the event has already been consumed, we can
- // still use the recently added finger event to determine whether to initiate
- // dragging the scene.
- it.changedToDownIgnoreConsumed() ||
+ // A) unconsumed AND recently pressed
+ it.changedToDown() ||
// B) unconsumed AND in a new position (on the current axis)
it.positionChange().toFloat() != 0f
}
@@ -461,4 +510,15 @@
}
}
}
+
+ private fun List<PointerInputChange>.countDown() = fastSumBy { if (it.pressed) 1 else 0 }
}
+
+internal fun interface PointersInfoOwner {
+ fun pointersInfo(): PointersInfo
+}
+
+internal data class PointersInfo(
+ val startedPosition: Offset?,
+ val pointersDown: Int,
+)
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
index 1fa6b3f7..ddff2f7 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
@@ -128,6 +128,7 @@
bottomOrRightBehavior: NestedScrollBehavior,
isExternalOverscrollGesture: () -> Boolean,
) : DelegatingNode() {
+ lateinit var pointersInfoOwner: PointersInfoOwner
private var priorityNestedScrollConnection: PriorityNestedScrollConnection =
scenePriorityNestedScrollConnection(
layoutImpl = layoutImpl,
@@ -135,6 +136,7 @@
topOrLeftBehavior = topOrLeftBehavior,
bottomOrRightBehavior = bottomOrRightBehavior,
isExternalOverscrollGesture = isExternalOverscrollGesture,
+ pointersInfoOwner = { pointersInfoOwner.pointersInfo() }
)
private var nestedScrollNode: DelegatableNode =
@@ -144,6 +146,7 @@
)
override fun onAttach() {
+ pointersInfoOwner = requireAncestorPointersInfoOwner()
delegate(nestedScrollNode)
}
@@ -171,6 +174,7 @@
topOrLeftBehavior = topOrLeftBehavior,
bottomOrRightBehavior = bottomOrRightBehavior,
isExternalOverscrollGesture = isExternalOverscrollGesture,
+ pointersInfoOwner = pointersInfoOwner,
)
nestedScrollNode =
nestedScrollModifierNode(
@@ -187,6 +191,7 @@
topOrLeftBehavior: NestedScrollBehavior,
bottomOrRightBehavior: NestedScrollBehavior,
isExternalOverscrollGesture: () -> Boolean,
+ pointersInfoOwner: PointersInfoOwner,
) =
NestedScrollHandlerImpl(
layoutImpl = layoutImpl,
@@ -194,5 +199,6 @@
topOrLeftBehavior = topOrLeftBehavior,
bottomOrRightBehavior = bottomOrRightBehavior,
isExternalOverscrollGesture = isExternalOverscrollGesture,
+ pointersInfoOwner = pointersInfoOwner,
)
.connection
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
index 65b388f..ff83d4b 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
@@ -113,7 +113,10 @@
orientation = draggableHandler.orientation,
topOrLeftBehavior = nestedScrollBehavior,
bottomOrRightBehavior = nestedScrollBehavior,
- isExternalOverscrollGesture = { isExternalOverscrollGesture }
+ isExternalOverscrollGesture = { isExternalOverscrollGesture },
+ pointersInfoOwner = {
+ PointersInfo(startedPosition = Offset.Zero, pointersDown = 1)
+ }
)
.connection
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
index 2de6faa..1ae9992 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
@@ -839,6 +839,80 @@
}
@Test
+ fun elementTransitionDuringNestedScrollWith2Pointers() {
+ // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is
+ // detected as a drag event.
+ var touchSlop = 0f
+ val translateY = 10.dp
+ val layoutWidth = 200.dp
+ val layoutHeight = 400.dp
+
+ val state =
+ rule.runOnUiThread {
+ MutableSceneTransitionLayoutState(
+ initialScene = SceneA,
+ transitions =
+ transitions {
+ from(SceneA, to = SceneB) {
+ translate(TestElements.Foo, y = translateY)
+ }
+ },
+ )
+ as MutableSceneTransitionLayoutStateImpl
+ }
+
+ rule.setContent {
+ touchSlop = LocalViewConfiguration.current.touchSlop
+ SceneTransitionLayout(
+ state = state,
+ modifier = Modifier.size(layoutWidth, layoutHeight)
+ ) {
+ scene(
+ SceneA,
+ userActions = mapOf(Swipe(SwipeDirection.Down, pointerCount = 2) to SceneB)
+ ) {
+ Box(
+ Modifier
+ // Unconsumed scroll gesture will be intercepted by STL
+ .verticalNestedScrollToScene()
+ // A scrollable that does not consume the scroll gesture
+ .scrollable(
+ rememberScrollableState(consumeScrollDelta = { 0f }),
+ Orientation.Vertical
+ )
+ .fillMaxSize()
+ ) {
+ Spacer(Modifier.element(TestElements.Foo).fillMaxSize())
+ }
+ }
+ scene(SceneB) { Spacer(Modifier.fillMaxSize()) }
+ }
+ }
+
+ assertThat(state.transitionState).isIdle()
+ val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag)
+ fooElement.assertTopPositionInRootIsEqualTo(0.dp)
+
+ // Swipe down with 2 pointers by half of verticalSwipeDistance.
+ rule.onRoot().performTouchInput {
+ val middleTop = Offset((layoutWidth / 2).toPx(), 0f)
+ repeat(2) { i -> down(pointerId = i, middleTop) }
+ repeat(2) { i ->
+ // Scroll 50%
+ moveBy(
+ pointerId = i,
+ delta = Offset(0f, touchSlop + layoutHeight.toPx() * 0.5f),
+ delayMillis = 1_000,
+ )
+ }
+ }
+
+ val transition = assertThat(state.transitionState).isTransition()
+ assertThat(transition).hasProgress(0.5f)
+ fooElement.assertTopPositionInRootIsEqualTo(translateY * 0.5f)
+ }
+
+ @Test
fun elementTransitionWithDistanceDuringOverscroll() {
val layoutWidth = 200.dp
val layoutHeight = 400.dp
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
index 460b640..ecafb17 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
@@ -128,7 +128,7 @@
var started = false
var dragged = false
var stopped = false
- var consumeBeforeMultiPointerDraggable = false
+ var consumedByDescendant = false
var touchSlop = 0f
rule.setContent {
@@ -138,6 +138,7 @@
.multiPointerDraggable(
orientation = Orientation.Vertical,
enabled = { true },
+ // We want to start a drag gesture immediately
startDragImmediately = { true },
onDragStarted = { _, _, _ ->
started = true
@@ -157,7 +158,7 @@
awaitPointerEventScope {
while (isActive) {
val change = awaitPointerEvent().changes.first()
- if (consumeBeforeMultiPointerDraggable) {
+ if (consumedByDescendant) {
change.consume()
}
}
@@ -168,18 +169,19 @@
}
// The first part of the gesture is consumed by our descendant
- consumeBeforeMultiPointerDraggable = true
+ consumedByDescendant = true
rule.onRoot().performTouchInput {
down(middle)
moveBy(Offset(0f, touchSlop))
}
- started = false
- dragged = false
- stopped = false
+ // The events were consumed by our descendant, we should not start a drag gesture.
+ assertThat(started).isFalse()
+ assertThat(dragged).isFalse()
+ assertThat(stopped).isFalse()
// The next events could be consumed by us
- consumeBeforeMultiPointerDraggable = false
+ consumedByDescendant = false
rule.onRoot().performTouchInput {
// The pointer is moved to a new position without reporting it
updatePointerBy(0, Offset(0f, touchSlop))
@@ -188,7 +190,7 @@
up()
}
- // This event should not be used to start a drag gesture
+ // The "up" event should not be used to start a drag gesture
assertThat(started).isFalse()
assertThat(dragged).isFalse()
assertThat(stopped).isFalse()
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
index de6f1cc..41bf630 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
@@ -314,7 +314,6 @@
}
},
)
- as MutableSceneTransitionLayoutStateImpl
// Default transition from A to B.
assertThat(state.setTargetScene(SceneB, coroutineScope = this)).isNotNull()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
index 86a99e0..6412276 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
@@ -32,11 +32,9 @@
import android.content.res.Resources;
import android.graphics.Region;
import android.os.Handler;
-import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.testing.TestableLooper.RunWithLooper;
import android.view.AttachedSurfaceControl;
-import android.view.View;
import android.view.ViewGroup;
import android.view.ViewRootImpl;
import android.view.ViewTreeObserver;
@@ -46,7 +44,6 @@
import com.android.dream.lowlight.LowLightTransitionCoordinator;
import com.android.keyguard.BouncerPanelExpansionCalculator;
-import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController;
import com.android.systemui.ambient.touch.scrim.BouncerlessScrimController;
@@ -101,9 +98,6 @@
ViewGroup mDreamOverlayContentView;
@Mock
- View mHubGestureIndicatorView;
-
- @Mock
Handler mHandler;
@Mock
@@ -158,7 +152,6 @@
mDreamOverlayContainerView,
mComplicationHostViewController,
mDreamOverlayContentView,
- mHubGestureIndicatorView,
mAmbientStatusBarViewController,
mLowLightTransitionCoordinator,
mTouchInsetSession,
@@ -179,18 +172,6 @@
mDreamManager);
}
- @DisableFlags(Flags.FLAG_COMMUNAL_HUB)
- @Test
- public void testHubGestureIndicatorGoneWhenFlagOff() {
- verify(mHubGestureIndicatorView, never()).setVisibility(View.VISIBLE);
- }
-
- @EnableFlags({Flags.FLAG_COMMUNAL_HUB, Flags.FLAG_GLANCEABLE_HUB_GESTURE_HANDLE})
- @Test
- public void testHubGestureIndicatorVisibleWhenFlagOn() {
- verify(mHubGestureIndicatorView).setVisibility(View.VISIBLE);
- }
-
@Test
public void testRootSurfaceControlInsetSetOnAttach() {
mController.onViewAttached();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractorTest.kt
index 0f5e458..8914c80 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractorTest.kt
@@ -41,6 +41,7 @@
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
@@ -54,6 +55,9 @@
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.scene.data.repository.Transition
+import com.android.systemui.scene.data.repository.setSceneTransition
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -208,6 +212,7 @@
}
@Test
+ @DisableSceneContainer
fun showWhenLockedActivityLaunchedFromPowerGesture_falseIfReturningToGone() =
testScope.runTest {
val values by collectValues(underTest.showWhenLockedActivityLaunchedFromPowerGesture)
@@ -245,6 +250,42 @@
@Test
@EnableSceneContainer
+ fun showWhenLockedActivityLaunchedFromPowerGesture_falseIfReturningToGone_scene_container() =
+ testScope.runTest {
+ val values by collectValues(underTest.showWhenLockedActivityLaunchedFromPowerGesture)
+ powerInteractor.setAwakeForTest()
+ kosmos.setSceneTransition(Transition(Scenes.Lockscreen, Scenes.Gone))
+
+ powerInteractor.setAsleepForTest()
+
+ kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Lockscreen))
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.AOD,
+ testScope = testScope,
+ throughTransitionState = TransitionState.RUNNING
+ )
+
+ powerInteractor.onCameraLaunchGestureDetected()
+ kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true)
+ powerInteractor.setAwakeForTest()
+ runCurrent()
+
+ kosmos.setSceneTransition(Transition(Scenes.Lockscreen, Scenes.Gone))
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.AOD,
+ to = KeyguardState.UNDEFINED,
+ testScope = testScope,
+ )
+
+ assertThat(values)
+ .containsExactly(
+ false,
+ )
+ }
+
+ @Test
+ @EnableSceneContainer
fun occludingActivityWillDismissKeyguard() =
testScope.runTest {
val occludingActivityWillDismissKeyguard by
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
index 40918a4..a8eccc5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
@@ -19,7 +19,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
import com.android.systemui.flags.DisableSceneContainer
@@ -42,19 +41,15 @@
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.data.repository.Idle
import com.android.systemui.scene.data.repository.Transition
-import com.android.systemui.scene.data.repository.sceneContainerRepository
import com.android.systemui.scene.data.repository.setSceneTransition
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertEquals
import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertThrows
-import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -67,36 +62,6 @@
val repository = kosmos.fakeKeyguardTransitionRepository
val testScope = kosmos.testScope
- private val sceneTransitions =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Idle(Scenes.Lockscreen)
- )
-
- private val lsToGone =
- ObservableTransitionState.Transition(
- Scenes.Lockscreen,
- Scenes.Gone,
- flowOf(Scenes.Lockscreen),
- flowOf(0f),
- false,
- flowOf(false)
- )
-
- private val goneToLs =
- ObservableTransitionState.Transition(
- Scenes.Gone,
- Scenes.Lockscreen,
- flowOf(Scenes.Lockscreen),
- flowOf(0f),
- false,
- flowOf(false)
- )
-
- @Before
- fun setUp() {
- kosmos.sceneContainerRepository.setTransitionState(sceneTransitions)
- }
-
@Test
fun transitionCollectorsReceivesOnlyAppropriateEvents() =
testScope.runTest {
@@ -318,7 +283,7 @@
@Test
fun isInTransitionToAnyState() =
testScope.runTest {
- val inTransition by collectValues(underTest.isInTransitionToAnyState)
+ val inTransition by collectValues(underTest.isInTransition)
assertEquals(
listOf(
@@ -374,9 +339,50 @@
}
@Test
+ @EnableSceneContainer
+ fun isInTransition_withScene() =
+ testScope.runTest {
+ val inTransition by collectValues(underTest.isInTransition)
+
+ assertEquals(
+ listOf(
+ false,
+ true, // The repo is seeded with a transition from OFF to LOCKSCREEN.
+ false,
+ ),
+ inTransition
+ )
+
+ kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Bouncer))
+
+ assertEquals(
+ listOf(
+ false,
+ true,
+ false,
+ true,
+ ),
+ inTransition
+ )
+
+ kosmos.setSceneTransition(Idle(Scenes.Bouncer))
+
+ assertEquals(
+ listOf(
+ false,
+ true,
+ false,
+ true,
+ false,
+ ),
+ inTransition
+ )
+ }
+
+ @Test
fun isInTransitionToAnyState_finishedStateIsStartedStateAfterCancels() =
testScope.runTest {
- val inTransition by collectValues(underTest.isInTransitionToAnyState)
+ val inTransition by collectValues(underTest.isInTransition)
assertEquals(
listOf(
@@ -731,92 +737,6 @@
}
@Test
- fun isInTransitionFromStateWhere() =
- testScope.runTest {
- val results by collectValues(underTest.isInTransitionFromStateWhere { it == DOZING })
-
- sendSteps(
- TransitionStep(AOD, DOZING, 0f, STARTED),
- TransitionStep(AOD, DOZING, 0.5f, RUNNING),
- TransitionStep(AOD, DOZING, 1f, FINISHED),
- )
-
- assertThat(results)
- .isEqualTo(
- listOf(
- false,
- )
- )
-
- sendSteps(
- TransitionStep(DOZING, GONE, 0f, STARTED),
- )
-
- assertThat(results)
- .isEqualTo(
- listOf(
- false,
- true,
- )
- )
-
- sendSteps(
- TransitionStep(DOZING, GONE, 0f, RUNNING),
- )
-
- assertThat(results)
- .isEqualTo(
- listOf(
- false,
- true,
- )
- )
-
- sendSteps(
- TransitionStep(DOZING, GONE, 0f, FINISHED),
- )
-
- assertThat(results)
- .isEqualTo(
- listOf(
- false,
- true,
- false,
- )
- )
-
- sendSteps(
- TransitionStep(GONE, DOZING, 0f, STARTED),
- TransitionStep(GONE, DOZING, 0f, RUNNING),
- TransitionStep(GONE, DOZING, 1f, FINISHED),
- )
-
- assertThat(results)
- .isEqualTo(
- listOf(
- false,
- true,
- false,
- )
- )
-
- sendSteps(
- TransitionStep(DOZING, GONE, 0f, STARTED),
- TransitionStep(DOZING, GONE, 0f, RUNNING),
- )
-
- assertThat(results)
- .isEqualTo(
- listOf(
- false,
- true,
- false,
- true,
- )
- )
- }
-
- @Test
fun isInTransitionWhere() =
testScope.runTest {
val results by
@@ -1599,7 +1519,7 @@
val currentStatesConverted by
collectValues(underTest.transition(Edge.create(LOCKSCREEN, UNDEFINED)))
- sceneTransitions.value = lsToGone
+ kosmos.setSceneTransition(Transition(Scenes.Lockscreen, Scenes.Gone))
val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED)
val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED)
val sendStep3 = TransitionStep(LOCKSCREEN, AOD, 0f, STARTED)
@@ -1615,7 +1535,7 @@
testScope.runTest {
val currentStates by collectValues(underTest.transition(Edge.create(LOCKSCREEN, GONE)))
- sceneTransitions.value = goneToLs
+ kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Lockscreen))
val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED)
val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED)
val sendStep3 = TransitionStep(LOCKSCREEN, AOD, 0f, STARTED)
@@ -1631,7 +1551,7 @@
val currentStates by
collectValues(underTest.transition(Edge.create(LOCKSCREEN, DOZING)))
- sceneTransitions.value = goneToLs
+ kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Lockscreen))
val sendStep1 = TransitionStep(LOCKSCREEN, DOZING, 0f, STARTED)
val sendStep2 = TransitionStep(LOCKSCREEN, DOZING, 1f, FINISHED)
val sendStep3 = TransitionStep(LOCKSCREEN, AOD, 0f, STARTED)
@@ -1648,7 +1568,7 @@
val currentStatesReversed by
collectValues(underTest.transition(Edge.create(null, LOCKSCREEN)))
- sceneTransitions.value = goneToLs
+ kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Lockscreen))
val sendStep1 = TransitionStep(LOCKSCREEN, DOZING, 0f, STARTED)
val sendStep2 = TransitionStep(LOCKSCREEN, DOZING, 1f, FINISHED)
val sendStep3 = TransitionStep(LOCKSCREEN, AOD, 0f, STARTED)
@@ -1666,7 +1586,7 @@
val currentStates by collectValues(underTest.transition(Edge.create(null, UNDEFINED)))
val currentStatesMapped by collectValues(underTest.transition(Edge.create(null, GONE)))
- sceneTransitions.value = lsToGone
+ kosmos.setSceneTransition(Transition(Scenes.Lockscreen, Scenes.Gone))
val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED)
val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED)
val sendStep3 = TransitionStep(UNDEFINED, AOD, 0f, STARTED)
@@ -1683,7 +1603,7 @@
testScope.runTest {
val currentStatesMapped by collectValues(underTest.transition(Edge.create(null, GONE)))
- sceneTransitions.value = goneToLs
+ kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Lockscreen))
val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED)
val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED)
val sendStep3 = TransitionStep(UNDEFINED, AOD, 0f, STARTED)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt
index 8b49d43..601779f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt
@@ -54,8 +54,10 @@
import com.android.systemui.settings.userTracker
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -103,9 +105,7 @@
appName2,
)
- private val underTest: EditModeViewModel by lazy {
- kosmos.editModeViewModel
- }
+ private val underTest: EditModeViewModel by lazy { kosmos.editModeViewModel }
@Before
fun setUp() {
@@ -461,31 +461,87 @@
}
@Test
- fun tileNotAvailable_notShowing() = with(kosmos) {
- testScope.runTest {
- val unavailableTile = "work"
- qsTileFactory = FakeQSFactory { spec ->
- FakeQSTile(userTracker.userId, spec != unavailableTile)
- }
- tileAvailabilityInteractorsMap = mapOf(
- unavailableTile to FakeTileAvailabilityInteractor(
- emptyMap<Int, Flow<Boolean>>().withDefault { flowOf(false) }
+ fun tileNotAvailable_notShowing() =
+ with(kosmos) {
+ testScope.runTest {
+ val unavailableTile = "work"
+ qsTileFactory = FakeQSFactory { spec ->
+ FakeQSTile(userTracker.userId, spec != unavailableTile)
+ }
+ tileAvailabilityInteractorsMap =
+ mapOf(
+ unavailableTile to
+ FakeTileAvailabilityInteractor(
+ emptyMap<Int, Flow<Boolean>>().withDefault { flowOf(false) }
+ )
)
- )
- val tiles by collectLastValue(underTest.tiles)
- val currentTiles =
+ val tiles by collectLastValue(underTest.tiles)
+ val currentTiles =
mutableListOf(
- TileSpec.create("flashlight"),
- TileSpec.create("airplane"),
- TileSpec.create("alarm"),
+ TileSpec.create("flashlight"),
+ TileSpec.create("airplane"),
+ TileSpec.create("alarm"),
)
- currentTilesInteractor.setTiles(currentTiles)
+ currentTilesInteractor.setTiles(currentTiles)
- underTest.startEditing()
+ underTest.startEditing()
- assertThat(tiles!!.none { it.tileSpec == TileSpec.create(unavailableTile) }).isTrue()
+ assertThat(tiles!!.none { it.tileSpec == TileSpec.create(unavailableTile) })
+ .isTrue()
+ }
}
- }
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ fun currentTiles_moveTileDown() =
+ with(kosmos) {
+ testScope.runTest {
+ val tiles by collectLastValue(underTest.tiles)
+ val currentTiles =
+ mutableListOf(
+ TileSpec.create("flashlight"),
+ TileSpec.create("airplane"),
+ TileSpec.create("internet"),
+ TileSpec.create("alarm"),
+ )
+ currentTilesInteractor.setTiles(currentTiles)
+ underTest.startEditing()
+ runCurrent()
+
+ // Move flashlight tile to index 3
+ underTest.addTile(TileSpec.create("flashlight"), 3)
+
+ assertThat(tiles!!.filter { it.isCurrent }.map { it.tileSpec.spec })
+ .containsExactly("airplane", "internet", "alarm", "flashlight")
+ .inOrder()
+ }
+ }
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ fun currentTiles_moveTileUp() =
+ with(kosmos) {
+ testScope.runTest {
+ val tiles by collectLastValue(underTest.tiles)
+ val currentTiles =
+ mutableListOf(
+ TileSpec.create("flashlight"),
+ TileSpec.create("airplane"),
+ TileSpec.create("internet"),
+ TileSpec.create("alarm"),
+ )
+ currentTilesInteractor.setTiles(currentTiles)
+ underTest.startEditing()
+ runCurrent()
+
+ // Move alarm tile to index 0
+ underTest.addTile(TileSpec.create("alarm"), 0)
+
+ assertThat(tiles!!.filter { it.isCurrent }.map { it.tileSpec.spec })
+ .containsExactly("alarm", "flashlight", "airplane", "internet")
+ .inOrder()
+ }
+ }
companion object {
private val drawable1 = TestStubDrawable("drawable1")
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
index 5242fe3..3acb328 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
@@ -24,6 +24,7 @@
import com.android.compose.animation.scene.SceneKey
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
@@ -62,6 +63,13 @@
private val underTest = kosmos.sceneInteractor
+ init {
+ // Init lazy Fixtures. Accessing them once makes sure that the singletons are initialized
+ // and therefore starts to collect StateFlows eagerly (when there are any).
+ kosmos.deviceUnlockedInteractor
+ kosmos.keyguardEnabledInteractor
+ }
+
@Test
fun allSceneKeys() {
assertThat(underTest.allSceneKeys()).isEqualTo(kosmos.sceneKeys)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinatorTest.kt
new file mode 100644
index 0000000..8a9720e
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinatorTest.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator
+
+import android.app.NotificationChannel
+import android.app.NotificationChannel.NEWS_ID
+import android.app.NotificationChannel.PROMOTIONS_ID
+import android.app.NotificationChannel.RECS_ID
+import android.app.NotificationChannel.SOCIAL_MEDIA_ID
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.render.NodeController
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper
+class BundleCoordinatorTest : SysuiTestCase() {
+ @Mock private lateinit var newsController: NodeController
+ @Mock private lateinit var socialController: NodeController
+ @Mock private lateinit var recsController: NodeController
+ @Mock private lateinit var promoController: NodeController
+
+ private lateinit var coordinator: BundleCoordinator
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ coordinator =
+ BundleCoordinator(
+ newsController,
+ socialController,
+ recsController,
+ promoController
+ )
+ }
+
+ @Test
+ fun newsSectioner() {
+ assertThat(coordinator.newsSectioner.isInSection(makeEntryOfChannelType(NEWS_ID))).isTrue()
+ assertThat(coordinator.newsSectioner.isInSection(makeEntryOfChannelType("news"))).isFalse()
+ }
+
+ @Test
+ fun socialSectioner() {
+ assertThat(coordinator.socialSectioner.isInSection(makeEntryOfChannelType(SOCIAL_MEDIA_ID)))
+ .isTrue()
+ assertThat(coordinator.socialSectioner.isInSection(makeEntryOfChannelType("social")))
+ .isFalse()
+ }
+
+ @Test
+ fun recsSectioner() {
+ assertThat(coordinator.recsSectioner.isInSection(makeEntryOfChannelType(RECS_ID))).isTrue()
+ assertThat(coordinator.recsSectioner.isInSection(makeEntryOfChannelType("recommendations")))
+ .isFalse()
+ }
+
+ @Test
+ fun promoSectioner() {
+ assertThat(coordinator.promoSectioner.isInSection(makeEntryOfChannelType(PROMOTIONS_ID)))
+ .isTrue()
+ assertThat(coordinator.promoSectioner.isInSection(makeEntryOfChannelType("promo"))).
+ isFalse()
+ }
+
+ private fun makeEntryOfChannelType(
+ type: String,
+ buildBlock: NotificationEntryBuilder.() -> Unit = {}
+ ): NotificationEntry {
+ val channel: NotificationChannel = NotificationChannel(type, type, 2)
+ val entry =
+ NotificationEntryBuilder()
+ .updateRanking {
+ it.setChannel(channel)
+ }
+ .also(buildBlock)
+ .build()
+ return entry
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt
index 6b5d072..495ab61 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.policy
import android.app.Notification
+import android.os.Handler
import android.platform.test.annotations.EnableFlags
import android.testing.TestableLooper.RunWithLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -59,6 +60,9 @@
// For creating TestableHeadsUpManager
@Mock private val mAccessibilityMgr: AccessibilityManagerWrapper? = null
private val mUiEventLoggerFake = UiEventLoggerFake()
+
+ @Mock private lateinit var mBgHandler: Handler
+
private val mLogger = Mockito.spy(HeadsUpManagerLogger(logcatLogBuffer()))
private val mGlobalSettings = FakeGlobalSettings()
private val mSystemClock = FakeSystemClock()
@@ -78,7 +82,7 @@
// Initialize AvalancheController and TestableHeadsUpManager during setUp instead of
// declaration, where mocks are null
- mAvalancheController = AvalancheController(dumpManager, mUiEventLoggerFake)
+ mAvalancheController = AvalancheController(dumpManager, mUiEventLoggerFake, mBgHandler)
testableHeadsUpManager =
TestableHeadsUpManager(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
index 206b39c..df07b44 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
@@ -38,6 +38,7 @@
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Person;
+import android.os.Handler;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.FlagsParameterization;
@@ -81,7 +82,7 @@
private UiEventLoggerFake mUiEventLoggerFake = new UiEventLoggerFake();
private final HeadsUpManagerLogger mLogger = spy(new HeadsUpManagerLogger(logcatLogBuffer()));
-
+ @Mock private Handler mBgHandler;
@Mock private DumpManager dumpManager;
private AvalancheController mAvalancheController;
@@ -148,7 +149,7 @@
@Override
public void SysuiSetup() throws Exception {
super.SysuiSetup();
- mAvalancheController = new AvalancheController(dumpManager, mUiEventLoggerFake);
+ mAvalancheController = new AvalancheController(dumpManager, mUiEventLoggerFake, mBgHandler);
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.java
index 7346323..3d3438e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.java
@@ -104,8 +104,7 @@
UiEventLogger uiEventLogger,
JavaAdapter javaAdapter,
ShadeInteractor shadeInteractor,
- AvalancheController avalancheController,
- Handler bgHandler
+ AvalancheController avalancheController
) {
super(
context,
@@ -123,8 +122,7 @@
uiEventLogger,
javaAdapter,
shadeInteractor,
- avalancheController,
- bgHandler
+ avalancheController
);
mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
mAutoDismissTime = TEST_AUTO_DISMISS_TIME;
@@ -147,8 +145,7 @@
mUiEventLogger,
mJavaAdapter,
mShadeInteractor,
- mAvalancheController,
- mBgHandler
+ mAvalancheController
);
}
@@ -173,7 +170,7 @@
mContext.getOrCreateTestableResources().addOverride(
R.integer.ambient_notification_extension_time, 500);
- mAvalancheController = new AvalancheController(dumpManager, mUiEventLogger);
+ mAvalancheController = new AvalancheController(dumpManager, mUiEventLogger, mBgHandler);
}
@Test
diff --git a/packages/SystemUI/res/drawable/hub_handle.xml b/packages/SystemUI/res/drawable/hub_handle.xml
deleted file mode 100644
index 8bc276f..0000000
--- a/packages/SystemUI/res/drawable/hub_handle.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright (C) 2024 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <corners android:radius="4dp" />
- <solid android:color="#FFFFFF" />
-</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/dream_overlay_container.xml b/packages/SystemUI/res/layout/dream_overlay_container.xml
index dcd3fa6..be1652b 100644
--- a/packages/SystemUI/res/layout/dream_overlay_container.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_container.xml
@@ -21,19 +21,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <ImageView
- android:id="@+id/glanceable_hub_handle"
- android:layout_width="4dp"
- android:layout_height="220dp"
- android:layout_centerVertical="true"
- android:layout_marginEnd="12dp"
- android:background="@drawable/hub_handle"
- android:visibility="gone"
- android:contentDescription="UI indicator for swiping open the glanceable hub"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toTopOf="parent" />
-
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/dream_overlay_content"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityRepository.kt b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityRepository.kt
index 6032f0b..b33924c 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityRepository.kt
@@ -18,10 +18,10 @@
import android.view.accessibility.AccessibilityManager
import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener
-import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import com.android.app.tracing.FlowTracing.tracedAwaitClose
+import com.android.app.tracing.FlowTracing.tracedConflatedCallbackFlow
import dagger.Module
import dagger.Provides
-import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -38,24 +38,28 @@
}
}
+private const val TAG = "AccessibilityRepository"
+
private class AccessibilityRepositoryImpl(
manager: AccessibilityManager,
) : AccessibilityRepository {
override val isTouchExplorationEnabled: Flow<Boolean> =
- conflatedCallbackFlow {
+ tracedConflatedCallbackFlow(TAG) {
val listener = TouchExplorationStateChangeListener(::trySend)
manager.addTouchExplorationStateChangeListener(listener)
trySend(manager.isTouchExplorationEnabled)
- awaitClose { manager.removeTouchExplorationStateChangeListener(listener) }
+ tracedAwaitClose(TAG) {
+ manager.removeTouchExplorationStateChangeListener(listener)
+ }
}
.distinctUntilChanged()
override val isEnabled: Flow<Boolean> =
- conflatedCallbackFlow {
+ tracedConflatedCallbackFlow(TAG) {
val listener = AccessibilityManager.AccessibilityStateChangeListener(::trySend)
manager.addAccessibilityStateChangeListener(listener)
trySend(manager.isEnabled)
- awaitClose { manager.removeAccessibilityStateChangeListener(listener) }
+ tracedAwaitClose(TAG) { manager.removeAccessibilityStateChangeListener(listener) }
}
.distinctUntilChanged()
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index d6d40f2..b466f31 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -995,6 +995,16 @@
}
/**
+ * @return true if ultrasonic udfps HW is supported on this device. Can return true even if
+ * the user has not enrolled udfps. This may be false if called before
+ * onAllAuthenticatorsRegistered.
+ */
+ public boolean isUltrasonicUdfpsSupported() {
+ return getUdfpsProps() != null && !getUdfpsProps().isEmpty() && getUdfpsProps()
+ .get(0).isUltrasonicUdfps();
+ }
+
+ /**
* @return true if sfps HW is supported on this device. Can return true even if the user has
* not enrolled sfps. This may be false if called before onAllAuthenticatorsRegistered.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 9d3c6a4..3dd3758 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -270,6 +270,7 @@
@Override
public void showUdfpsOverlay(long requestId, int sensorId, int reason,
@NonNull IUdfpsOverlayControllerCallback callback) {
+ mUdfpsOverlayInteractor.setRequestId(requestId);
mFgExecutor.execute(() -> UdfpsController.this.showUdfpsOverlay(
new UdfpsControllerOverlay(
mContext,
@@ -404,6 +405,15 @@
handler::post,
authenticationCallback);
}
+
+ /**
+ * Debug to run setIgnoreDisplayTouches
+ */
+ public void debugSetIgnoreDisplayTouches(boolean ignoreTouch) {
+ final long requestId = (mOverlay != null) ? mOverlay.getRequestId() : 0L;
+ UdfpsController.this.mFingerprintManager.setIgnoreDisplayTouches(
+ requestId, mSensorProps.sensorId, ignoreTouch);
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt
index f5e3d29..97ece11 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt
@@ -75,6 +75,8 @@
simFingerUp()
} else if (args.size == 1 && args[0] == "biometricPrompt") {
launchBiometricPrompt()
+ } else if (args.size == 2 && args[0] == "setIgnoreDisplayTouches") {
+ setIgnoreDisplayTouches(args[1].toBoolean())
} else {
invalidCommand(pw)
}
@@ -186,6 +188,11 @@
upEvent?.recycle()
}
+ @VisibleForTesting
+ fun setIgnoreDisplayTouches(ignoreTouches: Boolean) {
+ udfpsOverlayController?.debugSetIgnoreDisplayTouches(ignoreTouches)
+ }
+
private fun obtainMotionEvent(
action: Int,
x: Float,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
index a77cc1f..bb450c0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractor.kt
@@ -17,6 +17,7 @@
package com.android.systemui.biometrics.domain.interactor
import android.content.Context
+import android.hardware.fingerprint.FingerprintManager
import android.util.Log
import android.view.MotionEvent
import com.android.systemui.biometrics.AuthController
@@ -46,6 +47,7 @@
@Application private val context: Context,
private val authController: AuthController,
private val selectedUserInteractor: SelectedUserInteractor,
+ private val fingerprintManager: FingerprintManager?,
@Application scope: CoroutineScope
) {
private fun calculateIconSize(): Int {
@@ -70,8 +72,25 @@
return isUdfpsEnrolled && isWithinOverlayBounds
}
+ private var _requestId = MutableStateFlow(0L)
+
+ /** RequestId of current AcquisitionClient */
+ val requestId: StateFlow<Long> = _requestId.asStateFlow()
+
+ fun setRequestId(requestId: Long) {
+ _requestId.value = requestId
+ }
+
/** Sets whether Udfps overlay should handle touches */
fun setHandleTouches(shouldHandle: Boolean = true) {
+ if (authController.isUltrasonicUdfpsSupported
+ && shouldHandle != _shouldHandleTouches.value) {
+ fingerprintManager?.setIgnoreDisplayTouches(
+ requestId.value,
+ authController.udfpsProps!!.get(0).sensorId,
+ !shouldHandle
+ )
+ }
_shouldHandleTouches.value = shouldHandle
}
diff --git a/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt b/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt
index f991d5b..8270db1 100644
--- a/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt
+++ b/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt
@@ -16,7 +16,7 @@
package com.android.systemui.brightness.ui.compose
-import androidx.compose.animation.core.animateIntAsState
+import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
@@ -57,12 +57,12 @@
) {
var value by remember(gammaValue) { mutableIntStateOf(gammaValue) }
val animatedValue by
- animateIntAsState(targetValue = value, label = "BrightnessSliderAnimatedValue")
+ animateFloatAsState(targetValue = value.toFloat(), label = "BrightnessSliderAnimatedValue")
val floatValueRange = valueRange.first.toFloat()..valueRange.last.toFloat()
- val isRestricted = restriction is PolicyRestriction.Restricted
+ val isRestricted = remember(restriction) { restriction is PolicyRestriction.Restricted }
PlatformSlider(
- value = animatedValue.toFloat(),
+ value = animatedValue,
valueRange = floatValueRange,
enabled = !isRestricted,
onValueChange = {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index f9f01f7..780bf70 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -322,14 +322,6 @@
not(shadeInteractor.isAnyFullyExpanded)
.stateIn(bgScope, SharingStarted.Eagerly, initialValue = false)
- // TODO(b/339667383): remove this temporary swipe gesture handle
- /**
- * The dream overlay has its own gesture handle as the SysUI window is not visible above the
- * dream. This flow will be false when dreaming so that we don't show a duplicate handle when
- * opening the hub over the dream.
- */
- val showGestureIndicator: Flow<Boolean> = not(keyguardInteractor.isDreaming)
-
/** The type of background to use for the hub. */
val communalBackground: Flow<CommunalBackgroundType> =
communalSettingsInteractor.communalBackground
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
index 17202639..ed30d59 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -397,7 +397,7 @@
Pair(keyguardRepository.isKeyguardGoingAway.isFalse(), "keyguardNotGoingAway"),
Pair(
keyguardTransitionInteractor
- .isInTransitionToStateWhere(KeyguardState::deviceIsAsleepInState)
+ .isInTransitionWhere(toStatePredicate = KeyguardState::deviceIsAsleepInState)
.isFalse(),
"deviceNotTransitioningToAsleepState"
),
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
index 4fd1c24..76c7d23 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
@@ -21,8 +21,6 @@
import static com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress;
import static com.android.keyguard.BouncerPanelExpansionCalculator.getDreamAlphaScaledExpansion;
import static com.android.keyguard.BouncerPanelExpansionCalculator.getDreamYPositionScaledExpansion;
-import static com.android.systemui.Flags.communalHub;
-import static com.android.systemui.Flags.glanceableHubGestureHandle;
import static com.android.systemui.complication.ComplicationLayoutParams.POSITION_BOTTOM;
import static com.android.systemui.complication.ComplicationLayoutParams.POSITION_TOP;
import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
@@ -192,7 +190,6 @@
DreamOverlayContainerView containerView,
ComplicationHostViewController complicationHostViewController,
@Named(DreamOverlayModule.DREAM_OVERLAY_CONTENT_VIEW) ViewGroup contentView,
- @Named(DreamOverlayModule.HUB_GESTURE_INDICATOR_VIEW) View hubGestureIndicatorView,
AmbientStatusBarViewController statusBarViewController,
LowLightTransitionCoordinator lowLightTransitionCoordinator,
TouchInsetManager.TouchInsetSession touchInsetSession,
@@ -230,12 +227,6 @@
mComplicationHostViewController = complicationHostViewController;
mDreamOverlayMaxTranslationY = resources.getDimensionPixelSize(
R.dimen.dream_overlay_y_offset);
-
- if (communalHub() && glanceableHubGestureHandle()) {
- // TODO(b/339667383): remove this temporary swipe gesture handle
- hubGestureIndicatorView.setVisibility(View.VISIBLE);
- }
-
final View view = mComplicationHostViewController.getView();
mDreamOverlayContentView.addView(view,
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
index 76fcabd..12984efb 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
@@ -18,7 +18,6 @@
import android.content.res.Resources;
import android.view.LayoutInflater;
-import android.view.View;
import android.view.ViewGroup;
import androidx.lifecycle.Lifecycle;
@@ -42,7 +41,6 @@
@Module
public abstract class DreamOverlayModule {
public static final String DREAM_OVERLAY_CONTENT_VIEW = "dream_overlay_content_view";
- public static final String HUB_GESTURE_INDICATOR_VIEW = "hub_gesture_indicator_view";
public static final String MAX_BURN_IN_OFFSET = "max_burn_in_offset";
public static final String BURN_IN_PROTECTION_UPDATE_INTERVAL =
"burn_in_protection_update_interval";
@@ -75,18 +73,6 @@
"R.id.dream_overlay_content must not be null");
}
- /**
- * Gesture indicator bar on the right edge of the screen to indicate to users that they can
- * swipe to see their widgets on lock screen.
- */
- @Provides
- @DreamOverlayComponent.DreamOverlayScope
- @Named(HUB_GESTURE_INDICATOR_VIEW)
- public static View providesHubGestureIndicatorView(DreamOverlayContainerView view) {
- return Preconditions.checkNotNull(view.findViewById(R.id.glanceable_hub_handle),
- "R.id.glanceable_hub_handle must not be null");
- }
-
/** */
@Provides
public static TouchInsetManager.TouchInsetSession providesTouchInsetSession(
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepository.kt
index f54fd22..b47fb65 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepository.kt
@@ -16,12 +16,18 @@
package com.android.systemui.keyboard.shortcut.data.repository
+import android.content.Context
+import android.hardware.input.InputManager
+import android.util.Log
+import android.view.InputDevice
+import android.view.KeyCharacterMap
import android.view.KeyEvent
import android.view.KeyboardShortcutGroup
import android.view.KeyboardShortcutInfo
import android.view.WindowManager
import android.view.WindowManager.KeyboardShortcutsReceiver
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyboard.shortcut.data.source.KeyboardShortcutGroupsSource
import com.android.systemui.keyboard.shortcut.qualifiers.MultitaskingShortcuts
import com.android.systemui.keyboard.shortcut.qualifiers.SystemShortcuts
@@ -30,29 +36,46 @@
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.IME
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MULTI_TASKING
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.SYSTEM
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState.Active
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.withContext
@SysUISingleton
class ShortcutHelperCategoriesRepository
@Inject
constructor(
+ private val context: Context,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
@SystemShortcuts private val systemShortcutsSource: KeyboardShortcutGroupsSource,
@MultitaskingShortcuts private val multitaskingShortcutsSource: KeyboardShortcutGroupsSource,
private val windowManager: WindowManager,
- shortcutHelperStateRepository: ShortcutHelperStateRepository
+ private val inputManager: InputManager,
+ stateRepository: ShortcutHelperStateRepository
) {
- val systemShortcutsCategory =
- shortcutHelperStateRepository.state.map {
+ private val activeInputDevice =
+ stateRepository.state.map {
if (it is Active) {
+ withContext(backgroundDispatcher) { inputManager.getInputDevice(it.deviceId) }
+ } else {
+ null
+ }
+ }
+
+ val systemShortcutsCategory =
+ activeInputDevice.map {
+ if (it != null) {
toShortcutCategory(
- systemShortcutsSource.shortcutGroups(),
- ShortcutCategoryType.SYSTEM
+ it.keyCharacterMap,
+ SYSTEM,
+ systemShortcutsSource.shortcutGroups()
)
} else {
null
@@ -60,76 +83,128 @@
}
val multitaskingShortcutsCategory =
- shortcutHelperStateRepository.state.map {
- if (it is Active) {
- toShortcutCategory(multitaskingShortcutsSource.shortcutGroups(), MULTI_TASKING)
+ activeInputDevice.map {
+ if (it != null) {
+ toShortcutCategory(
+ it.keyCharacterMap,
+ MULTI_TASKING,
+ multitaskingShortcutsSource.shortcutGroups()
+ )
} else {
null
}
}
val imeShortcutsCategory =
- shortcutHelperStateRepository.state.map {
- if (it is Active) retrieveImeShortcuts(it.deviceId) else null
- }
+ activeInputDevice.map { if (it != null) retrieveImeShortcuts(it) else null }
- private suspend fun retrieveImeShortcuts(deviceId: Int): ShortcutCategory? {
+ private suspend fun retrieveImeShortcuts(
+ inputDevice: InputDevice,
+ ): ShortcutCategory? {
return suspendCancellableCoroutine { continuation ->
val shortcutsReceiver = KeyboardShortcutsReceiver { shortcutGroups ->
- continuation.resumeWith(Result.success(toShortcutCategory(shortcutGroups, IME)))
+ continuation.resumeWith(
+ Result.success(
+ toShortcutCategory(inputDevice.keyCharacterMap, IME, shortcutGroups)
+ )
+ )
}
- windowManager.requestImeKeyboardShortcuts(shortcutsReceiver, deviceId)
+ windowManager.requestImeKeyboardShortcuts(shortcutsReceiver, inputDevice.id)
}
}
private fun toShortcutCategory(
- shortcutGroups: List<KeyboardShortcutGroup>,
+ keyCharacterMap: KeyCharacterMap,
type: ShortcutCategoryType,
+ shortcutGroups: List<KeyboardShortcutGroup>,
): ShortcutCategory? {
val subCategories =
shortcutGroups
.map { shortcutGroup ->
ShortcutSubCategory(
- label = shortcutGroup.label.toString(),
- shortcuts = toShortcuts(shortcutGroup.items)
+ shortcutGroup.label.toString(),
+ toShortcuts(keyCharacterMap, shortcutGroup.items)
)
}
.filter { it.shortcuts.isNotEmpty() }
return if (subCategories.isEmpty()) {
+ Log.wtf(TAG, "Empty sub categories after converting $shortcutGroups")
null
} else {
ShortcutCategory(type, subCategories)
}
}
- private fun toShortcuts(infoList: List<KeyboardShortcutInfo>) =
- infoList.mapNotNull { toShortcut(it) }
+ private fun toShortcuts(
+ keyCharacterMap: KeyCharacterMap,
+ infoList: List<KeyboardShortcutInfo>
+ ) = infoList.mapNotNull { toShortcut(keyCharacterMap, it) }
- private fun toShortcut(shortcutInfo: KeyboardShortcutInfo): Shortcut? {
- val shortcutCommand = toShortcutCommand(shortcutInfo)
+ private fun toShortcut(
+ keyCharacterMap: KeyCharacterMap,
+ shortcutInfo: KeyboardShortcutInfo
+ ): Shortcut? {
+ val shortcutCommand = toShortcutCommand(keyCharacterMap, shortcutInfo)
return if (shortcutCommand == null) null
else Shortcut(label = shortcutInfo.label!!.toString(), commands = listOf(shortcutCommand))
}
- private fun toShortcutCommand(info: KeyboardShortcutInfo): ShortcutCommand? {
- val keyCodes = mutableListOf<Int>()
+ private fun toShortcutCommand(
+ keyCharacterMap: KeyCharacterMap,
+ info: KeyboardShortcutInfo
+ ): ShortcutCommand? {
+ val keys = mutableListOf<ShortcutKey>()
var remainingModifiers = info.modifiers
SUPPORTED_MODIFIERS.forEach { supportedModifier ->
if ((supportedModifier and remainingModifiers) != 0) {
- keyCodes += supportedModifier
+ keys += toShortcutKey(keyCharacterMap, supportedModifier) ?: return null
// "Remove" the modifier from the remaining modifiers
remainingModifiers = remainingModifiers and supportedModifier.inv()
}
}
if (remainingModifiers != 0) {
// There is a remaining modifier we don't support
+ Log.wtf(TAG, "Unsupported modifiers remaining: $remainingModifiers")
return null
}
- keyCodes += info.keycode
- return ShortcutCommand(keyCodes)
+ if (info.keycode != 0) {
+ keys += toShortcutKey(keyCharacterMap, info.keycode, info.baseCharacter) ?: return null
+ }
+ if (keys.isEmpty()) {
+ Log.wtf(TAG, "No keys for $info")
+ return null
+ }
+ return ShortcutCommand(keys)
+ }
+
+ private fun toShortcutKey(
+ keyCharacterMap: KeyCharacterMap,
+ keyCode: Int,
+ baseCharacter: Char = Char.MIN_VALUE,
+ ): ShortcutKey? {
+ val iconResId = ShortcutHelperKeys.keyIcons[keyCode]
+ if (iconResId != null) {
+ return ShortcutKey.Icon(iconResId)
+ }
+ if (baseCharacter > Char.MIN_VALUE) {
+ return ShortcutKey.Text(baseCharacter.toString())
+ }
+ val specialKeyLabel = ShortcutHelperKeys.specialKeyLabels[keyCode]
+ if (specialKeyLabel != null) {
+ val label = specialKeyLabel(context)
+ return ShortcutKey.Text(label)
+ }
+ val displayLabelCharacter = keyCharacterMap.getDisplayLabel(keyCode)
+ if (displayLabelCharacter.code != 0) {
+ return ShortcutKey.Text(displayLabelCharacter.toString())
+ }
+ Log.wtf(TAG, "Couldn't find label or icon for key: $keyCode")
+ return null
}
companion object {
+ private const val TAG = "SHCategoriesRepo"
+
private val SUPPORTED_MODIFIERS =
listOf(
KeyEvent.META_META_ON,
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt
index e5b870a..adc6d95 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/Shortcut.kt
@@ -21,8 +21,8 @@
class ShortcutBuilder(private val label: String) {
val commands = mutableListOf<ShortcutCommand>()
- fun command(vararg keyCodes: Int) {
- commands += ShortcutCommand(keyCodes.toList())
+ fun command(builder: ShortcutCommandBuilder.() -> Unit) {
+ commands += ShortcutCommandBuilder().apply(builder).build()
}
fun build() = Shortcut(label, commands)
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCategory.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCategory.kt
index 3ac7fa8..5d05359 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCategory.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCategory.kt
@@ -30,8 +30,8 @@
class ShortcutCategoryBuilder(val type: ShortcutCategoryType) {
private val subCategories = mutableListOf<ShortcutSubCategory>()
- fun subCategory(label: String, shortcuts: List<Shortcut>) {
- subCategories += ShortcutSubCategory(label, shortcuts)
+ fun subCategory(label: String, builder: ShortcutSubCategoryBuilder.() -> Unit) {
+ subCategories += ShortcutSubCategoryBuilder(label).apply(builder).build()
}
fun build() = ShortcutCategory(type, subCategories)
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt
index a98a8ff..e5b8096 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt
@@ -16,4 +16,23 @@
package com.android.systemui.keyboard.shortcut.shared.model
-data class ShortcutCommand(val keyCodes: List<Int>)
+import androidx.annotation.DrawableRes
+
+data class ShortcutCommand(val keys: List<ShortcutKey>)
+
+class ShortcutCommandBuilder {
+ private val keys = mutableListOf<ShortcutKey>()
+
+ fun key(text: String) {
+ keys += ShortcutKey.Text(text)
+ }
+
+ fun key(@DrawableRes drawableResId: Int) {
+ keys += ShortcutKey.Icon(drawableResId)
+ }
+
+ fun build() = ShortcutCommand(keys)
+}
+
+fun shortcutCommand(block: ShortcutCommandBuilder.() -> Unit) =
+ ShortcutCommandBuilder().apply(block).build()
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutKey.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutKey.kt
new file mode 100644
index 0000000..1abb78c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutKey.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.shared.model
+
+import androidx.annotation.DrawableRes
+
+sealed interface ShortcutKey {
+ data class Text(val value: String) : ShortcutKey
+
+ data class Icon(@DrawableRes val drawableResId: Int) : ShortcutKey
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutSubCategory.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutSubCategory.kt
index 4545b4c..1401678 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutSubCategory.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutSubCategory.kt
@@ -17,3 +17,13 @@
package com.android.systemui.keyboard.shortcut.shared.model
data class ShortcutSubCategory(val label: String, val shortcuts: List<Shortcut>)
+
+class ShortcutSubCategoryBuilder(val label: String) {
+ private val shortcuts = mutableListOf<Shortcut>()
+
+ fun shortcut(label: String, builder: ShortcutBuilder.() -> Unit) {
+ shortcuts += ShortcutBuilder(label).apply(builder).build()
+ }
+
+ fun build() = ShortcutSubCategory(label, shortcuts)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 5f6fe1c..b32d0950 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -27,6 +27,7 @@
import android.os.Handler
import android.os.PowerManager
import android.os.RemoteException
+import android.os.Trace
import android.util.Log
import android.view.RemoteAnimationTarget
import android.view.SurfaceControl
@@ -385,10 +386,15 @@
valueAnimator.animatedValue as Float, openingWallpaperTargets)
}
addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationStart(animation: Animator) {
+ super.onAnimationStart(animation)
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_APP, "WallpaperAlphaAnimation", 0)
+ }
override fun onAnimationEnd(animation: Animator) {
Log.d(TAG, "wallpaperCannedUnlockAnimator#onAnimationEnd")
keyguardViewMediator.get().exitKeyguardAndFinishSurfaceBehindRemoteAnimation(
false /* cancelled */)
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_APP, "WallpaperAlphaAnimation", 0)
}
})
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt
index e9bd889..e6bab4c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceRemoteUserSelectionManager.kt
@@ -19,8 +19,9 @@
import android.content.Context
import android.os.UserHandle
+import com.android.app.tracing.FlowTracing.tracedAwaitClose
+import com.android.app.tracing.FlowTracing.tracedConflatedCallbackFlow
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.settings.UserTracker
@@ -28,7 +29,6 @@
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
@@ -55,19 +55,20 @@
private val userHandle: UserHandle,
) : KeyguardQuickAffordanceSelectionManager {
- private val userId: Flow<Int> = conflatedCallbackFlow {
- val callback =
- object : UserTracker.Callback {
- override fun onUserChanged(newUser: Int, userContext: Context) {
- trySendWithFailureLogging(newUser, TAG)
+ private val userId: Flow<Int> =
+ tracedConflatedCallbackFlow("userId") {
+ val callback =
+ object : UserTracker.Callback {
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ trySendWithFailureLogging(newUser, TAG)
+ }
}
- }
- userTracker.addCallback(callback) { it.run() }
- trySendWithFailureLogging(userTracker.userId, TAG)
+ userTracker.addCallback(callback) { it.run() }
+ trySendWithFailureLogging(userTracker.userId, TAG)
- awaitClose { userTracker.removeCallback(callback) }
- }
+ tracedAwaitClose("userId") { userTracker.removeCallback(callback) }
+ }
private val clientOrNull: StateFlow<CustomizationProviderClient?> =
userId
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt
index 1fe94d5..a145214 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt
@@ -21,7 +21,6 @@
import android.media.AudioManager
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
-import com.android.systemui.res.R
import com.android.systemui.animation.Expandable
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.common.shared.model.ContentDescription
@@ -31,6 +30,7 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
+import com.android.systemui.res.R
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.RingerModeTracker
@@ -86,8 +86,8 @@
audioManager.isVolumeFixed ->
ActivationState.NotSupported to R.string.volume_ringer_hint_mute
mode == AudioManager.RINGER_MODE_SILENT ->
- ActivationState.Active to R.string.volume_ringer_hint_mute
- else -> ActivationState.Inactive to R.string.volume_ringer_hint_unmute
+ ActivationState.Active to R.string.volume_ringer_hint_unmute
+ else -> ActivationState.Inactive to R.string.volume_ringer_hint_mute
}
KeyguardQuickAffordanceConfig.LockScreenState.Visible(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index 63dd255..f837d8e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -62,6 +62,7 @@
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
/** Defines interface for classes that encapsulate application state for the keyguard. */
interface KeyguardRepository {
@@ -362,30 +363,8 @@
override val topClippingBounds = MutableStateFlow<Int?>(null)
- override val isKeyguardShowing: Flow<Boolean> =
- conflatedCallbackFlow {
- val callback =
- object : KeyguardStateController.Callback {
- override fun onKeyguardShowingChanged() {
- trySendWithFailureLogging(
- keyguardStateController.isShowing,
- TAG,
- "updated isKeyguardShowing"
- )
- }
- }
-
- keyguardStateController.addCallback(callback)
- // Adding the callback does not send an initial update.
- trySendWithFailureLogging(
- keyguardStateController.isShowing,
- TAG,
- "initial isKeyguardShowing"
- )
-
- awaitClose { keyguardStateController.removeCallback(callback) }
- }
- .distinctUntilChanged()
+ override val isKeyguardShowing: MutableStateFlow<Boolean> =
+ MutableStateFlow(keyguardStateController.isShowing)
private val _isAodAvailable = MutableStateFlow(false)
override val isAodAvailable: StateFlow<Boolean> = _isAodAvailable.asStateFlow()
@@ -394,91 +373,14 @@
_isAodAvailable.value = value
}
- override val isKeyguardOccluded: Flow<Boolean> =
- conflatedCallbackFlow {
- val callback =
- object : KeyguardStateController.Callback {
- override fun onKeyguardShowingChanged() {
- trySendWithFailureLogging(
- keyguardStateController.isOccluded,
- TAG,
- "updated isKeyguardOccluded"
- )
- }
- }
+ override val isKeyguardOccluded: MutableStateFlow<Boolean> =
+ MutableStateFlow(keyguardStateController.isOccluded)
- keyguardStateController.addCallback(callback)
- // Adding the callback does not send an initial update.
- trySendWithFailureLogging(
- keyguardStateController.isOccluded,
- TAG,
- "initial isKeyguardOccluded"
- )
+ override val isKeyguardDismissible: MutableStateFlow<Boolean> =
+ MutableStateFlow(keyguardStateController.isUnlocked)
- awaitClose { keyguardStateController.removeCallback(callback) }
- }
- .distinctUntilChanged()
-
- override val isKeyguardDismissible: StateFlow<Boolean> =
- conflatedCallbackFlow {
- val callback =
- object : KeyguardStateController.Callback {
- override fun onUnlockedChanged() {
- trySendWithFailureLogging(
- keyguardStateController.isUnlocked,
- TAG,
- "updated isKeyguardDismissible due to onUnlockedChanged"
- )
- }
-
- override fun onKeyguardShowingChanged() {
- trySendWithFailureLogging(
- keyguardStateController.isUnlocked,
- TAG,
- "updated isKeyguardDismissible due to onKeyguardShowingChanged"
- )
- }
- }
-
- keyguardStateController.addCallback(callback)
- // Adding the callback does not send an initial update.
- trySendWithFailureLogging(
- keyguardStateController.isUnlocked,
- TAG,
- "initial isKeyguardUnlocked"
- )
-
- awaitClose { keyguardStateController.removeCallback(callback) }
- }
- .distinctUntilChanged()
- .stateIn(
- scope,
- SharingStarted.Eagerly,
- initialValue = false,
- )
-
- override val isKeyguardGoingAway: Flow<Boolean> = conflatedCallbackFlow {
- val callback =
- object : KeyguardStateController.Callback {
- override fun onKeyguardGoingAwayChanged() {
- trySendWithFailureLogging(
- keyguardStateController.isKeyguardGoingAway,
- TAG,
- "updated isKeyguardGoingAway"
- )
- }
- }
-
- keyguardStateController.addCallback(callback)
- // Adding the callback does not send an initial update.
- trySendWithFailureLogging(
- keyguardStateController.isKeyguardGoingAway,
- TAG,
- "initial isKeyguardGoingAway"
- )
-
- awaitClose { keyguardStateController.removeCallback(callback) }
- }
+ override val isKeyguardGoingAway: MutableStateFlow<Boolean> =
+ MutableStateFlow(keyguardStateController.isKeyguardGoingAway)
private val _isKeyguardEnabled =
MutableStateFlow(!lockPatternUtils.isLockScreenDisabled(userTracker.userId))
@@ -669,6 +571,35 @@
private val _isActiveDreamLockscreenHosted = MutableStateFlow(false)
override val isActiveDreamLockscreenHosted = _isActiveDreamLockscreenHosted.asStateFlow()
+ init {
+ val callback =
+ object : KeyguardStateController.Callback {
+ override fun onKeyguardShowingChanged() {
+ isKeyguardShowing.value = keyguardStateController.isShowing
+ isKeyguardOccluded.value = keyguardStateController.isOccluded
+ isKeyguardDismissible.value = keyguardStateController.isUnlocked
+ }
+
+ override fun onUnlockedChanged() {
+ isKeyguardDismissible.value = keyguardStateController.isUnlocked
+ }
+
+ override fun onKeyguardGoingAwayChanged() {
+ isKeyguardGoingAway.value = keyguardStateController.isKeyguardGoingAway
+ }
+ }
+
+ keyguardStateController.addCallback(callback)
+
+ scope
+ .launch {
+ isKeyguardShowing.collect {
+ // no-op to allow for callback removal
+ }
+ }
+ .invokeOnCompletion { keyguardStateController.removeCallback(callback) }
+ }
+
override fun setAnimateDozingTransitions(animate: Boolean) {
_animateBottomAreaDozingTransitions.value = animate
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
index 5573f0d..b44a8cf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
@@ -56,6 +56,7 @@
@Inject
constructor(
override val transitionRepository: KeyguardTransitionRepository,
+ override val internalTransitionInteractor: InternalKeyguardTransitionInteractor,
transitionInteractor: KeyguardTransitionInteractor,
@Background private val scope: CoroutineScope,
@Background bgDispatcher: CoroutineDispatcher,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
index 2a9ee9f..868c462 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
@@ -44,6 +44,7 @@
@Inject
constructor(
override val transitionRepository: KeyguardTransitionRepository,
+ override val internalTransitionInteractor: InternalKeyguardTransitionInteractor,
transitionInteractor: KeyguardTransitionInteractor,
@Background private val scope: CoroutineScope,
@Background bgDispatcher: CoroutineDispatcher,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
index b423ed9..76e88a2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
@@ -28,6 +28,7 @@
import com.android.systemui.keyguard.shared.model.BiometricUnlockMode.Companion.isWakeAndUnlock
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.util.kotlin.Utils.Companion.sample
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
@@ -44,6 +45,7 @@
@Inject
constructor(
override val transitionRepository: KeyguardTransitionRepository,
+ override val internalTransitionInteractor: InternalKeyguardTransitionInteractor,
transitionInteractor: KeyguardTransitionInteractor,
@Background private val scope: CoroutineScope,
@Background bgDispatcher: CoroutineDispatcher,
@@ -99,10 +101,14 @@
biometricUnlockState,
) ->
if (isWakeAndUnlock(biometricUnlockState.mode)) {
- startTransitionTo(
- KeyguardState.GONE,
- ownerReason = "biometric wake and unlock",
- )
+ if (SceneContainerFlag.isEnabled) {
+ // TODO(b/336576536): Check if adaptation for scene framework is needed
+ } else {
+ startTransitionTo(
+ KeyguardState.GONE,
+ ownerReason = "biometric wake and unlock",
+ )
+ }
}
}
}
@@ -130,21 +136,35 @@
isIdleOnCommunal,
canTransitionToGoneOnWake,
primaryBouncerShowing) ->
- startTransitionTo(
- if (!deviceEntryRepository.isLockscreenEnabled()) {
- KeyguardState.GONE
- } else if (canTransitionToGoneOnWake) {
- KeyguardState.GONE
- } else if (primaryBouncerShowing) {
- KeyguardState.PRIMARY_BOUNCER
- } else if (occluded) {
- KeyguardState.OCCLUDED
- } else if (isIdleOnCommunal) {
- KeyguardState.GLANCEABLE_HUB
+ if (!deviceEntryRepository.isLockscreenEnabled()) {
+ if (SceneContainerFlag.isEnabled) {
+ // TODO(b/336576536): Check if adaptation for scene framework is needed
} else {
- KeyguardState.LOCKSCREEN
+ startTransitionTo(KeyguardState.GONE)
}
- )
+ } else if (canTransitionToGoneOnWake) {
+ if (SceneContainerFlag.isEnabled) {
+ // TODO(b/336576536): Check if adaptation for scene framework is needed
+ } else {
+ startTransitionTo(KeyguardState.GONE)
+ }
+ } else if (primaryBouncerShowing) {
+ if (SceneContainerFlag.isEnabled) {
+ // TODO(b/336576536): Check if adaptation for scene framework is needed
+ } else {
+ startTransitionTo(KeyguardState.PRIMARY_BOUNCER)
+ }
+ } else if (occluded) {
+ startTransitionTo(KeyguardState.OCCLUDED)
+ } else if (isIdleOnCommunal) {
+ if (SceneContainerFlag.isEnabled) {
+ // TODO(b/336576536): Check if adaptation for scene framework is needed
+ } else {
+ startTransitionTo(KeyguardState.GLANCEABLE_HUB)
+ }
+ } else {
+ startTransitionTo(KeyguardState.LOCKSCREEN)
+ }
}
}
}
@@ -176,23 +196,55 @@
// Handled by dismissFromDozing().
!isWakeAndUnlock(biometricUnlockState.mode)
) {
- startTransitionTo(
- if (!KeyguardWmStateRefactor.isEnabled && canDismissLockscreen) {
- KeyguardState.GONE
- } else if (
- KeyguardWmStateRefactor.isEnabled &&
- !deviceEntryRepository.isLockscreenEnabled()
- ) {
- KeyguardState.GONE
- } else if (primaryBouncerShowing) {
- KeyguardState.PRIMARY_BOUNCER
- } else if (isIdleOnCommunal) {
- KeyguardState.GLANCEABLE_HUB
+ if (!KeyguardWmStateRefactor.isEnabled && canDismissLockscreen) {
+ if (SceneContainerFlag.isEnabled) {
+ // TODO(b/336576536): Check if adaptation for scene framework is
+ // needed
} else {
- KeyguardState.LOCKSCREEN
- },
- ownerReason = "waking from dozing"
- )
+ startTransitionTo(
+ KeyguardState.GONE,
+ ownerReason = "waking from dozing"
+ )
+ }
+ } else if (
+ KeyguardWmStateRefactor.isEnabled &&
+ !deviceEntryRepository.isLockscreenEnabled()
+ ) {
+ if (SceneContainerFlag.isEnabled) {
+ // TODO(b/336576536): Check if adaptation for scene framework is
+ // needed
+ } else {
+ startTransitionTo(
+ KeyguardState.GONE,
+ ownerReason = "waking from dozing"
+ )
+ }
+ } else if (primaryBouncerShowing) {
+ if (SceneContainerFlag.isEnabled) {
+ // TODO(b/336576536): Check if adaptation for scene framework is
+ // needed
+ } else {
+ startTransitionTo(
+ KeyguardState.PRIMARY_BOUNCER,
+ ownerReason = "waking from dozing"
+ )
+ }
+ } else if (isIdleOnCommunal) {
+ if (SceneContainerFlag.isEnabled) {
+ // TODO(b/336576536): Check if adaptation for scene framework is
+ // needed
+ } else {
+ startTransitionTo(
+ KeyguardState.GLANCEABLE_HUB,
+ ownerReason = "waking from dozing"
+ )
+ }
+ } else {
+ startTransitionTo(
+ KeyguardState.LOCKSCREEN,
+ ownerReason = "waking from dozing"
+ )
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt
index 47aa02a..117dbcf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt
@@ -41,6 +41,7 @@
@Inject
constructor(
override val transitionRepository: KeyguardTransitionRepository,
+ override val internalTransitionInteractor: InternalKeyguardTransitionInteractor,
transitionInteractor: KeyguardTransitionInteractor,
@Background private val scope: CoroutineScope,
@Background bgDispatcher: CoroutineDispatcher,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index 7fa197c..0e76487 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -47,6 +47,7 @@
@Inject
constructor(
override val transitionRepository: KeyguardTransitionRepository,
+ override val internalTransitionInteractor: InternalKeyguardTransitionInteractor,
transitionInteractor: KeyguardTransitionInteractor,
@Background private val scope: CoroutineScope,
@Background bgDispatcher: CoroutineDispatcher,
@@ -90,8 +91,8 @@
private fun listenForDreamingToGlanceableHub() {
if (!communalHub()) return
- if (SceneContainerFlag.isEnabled)
- return // TODO(b/336576536): Check if adaptation for scene framework is needed
+ // TODO(b/336576536): Check if adaptation for scene framework is needed
+ if (SceneContainerFlag.isEnabled) return
scope.launch("$TAG#listenForDreamingToGlanceableHub", mainDispatcher) {
glanceableHubTransitions.listenForGlanceableHubTransition(
transitionOwnerName = TAG,
@@ -102,6 +103,8 @@
}
private fun listenForDreamingToPrimaryBouncer() {
+ // TODO(b/336576536): Check if adaptation for scene framework is needed
+ if (SceneContainerFlag.isEnabled) return
scope.launch {
keyguardInteractor.primaryBouncerShowing
.sample(startedKeyguardTransitionStep, ::Pair)
@@ -178,8 +181,8 @@
}
private fun listenForDreamingToGoneWhenDismissable() {
- if (SceneContainerFlag.isEnabled)
- return // TODO(b/336576536): Check if adaptation for scene framework is needed
+ // TODO(b/336576536): Check if adaptation for scene framework is needed
+ if (SceneContainerFlag.isEnabled) return
scope.launch {
keyguardInteractor.isAbleToDream
.sampleCombine(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
index e516fa3..8041dd9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
@@ -50,6 +50,7 @@
private val glanceableHubTransitions: GlanceableHubTransitions,
keyguardInteractor: KeyguardInteractor,
override val transitionRepository: KeyguardTransitionRepository,
+ override val internalTransitionInteractor: InternalKeyguardTransitionInteractor,
transitionInteractor: KeyguardTransitionInteractor,
powerInteractor: PowerInteractor,
keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index 07a2b04..b084824 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -45,6 +45,7 @@
@Inject
constructor(
override val transitionRepository: KeyguardTransitionRepository,
+ override val internalTransitionInteractor: InternalKeyguardTransitionInteractor,
transitionInteractor: KeyguardTransitionInteractor,
@Background private val scope: CoroutineScope,
@Background bgDispatcher: CoroutineDispatcher,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 35a2d58..ff15a1b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -57,6 +57,7 @@
@Inject
constructor(
override val transitionRepository: KeyguardTransitionRepository,
+ override val internalTransitionInteractor: InternalKeyguardTransitionInteractor,
transitionInteractor: KeyguardTransitionInteractor,
@Background private val scope: CoroutineScope,
@Background bgDispatcher: CoroutineDispatcher,
@@ -128,7 +129,7 @@
keyguardInteractor.isAbleToDream
.filterRelevantKeyguardState()
.sampleCombine(
- transitionInteractor.currentTransitionInfoInternal,
+ internalTransitionInteractor.currentTransitionInfoInternal,
finishedKeyguardState,
keyguardInteractor.isActiveDreamLockscreenHosted,
)
@@ -185,7 +186,7 @@
shadeRepository.legacyShadeExpansion
.sampleCombine(
startedKeyguardTransitionStep,
- transitionInteractor.currentTransitionInfoInternal,
+ internalTransitionInteractor.currentTransitionInfoInternal,
keyguardInteractor.statusBarState,
keyguardInteractor.isKeyguardDismissible,
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
index 86d4cfb..84ca667 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
@@ -41,6 +41,7 @@
@Inject
constructor(
override val transitionRepository: KeyguardTransitionRepository,
+ override val internalTransitionInteractor: InternalKeyguardTransitionInteractor,
transitionInteractor: KeyguardTransitionInteractor,
@Background private val scope: CoroutineScope,
@Background bgDispatcher: CoroutineDispatcher,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index f23b12f..f98ed15 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -50,6 +50,7 @@
@Inject
constructor(
override val transitionRepository: KeyguardTransitionRepository,
+ override val internalTransitionInteractor: InternalKeyguardTransitionInteractor,
transitionInteractor: KeyguardTransitionInteractor,
@Background private val scope: CoroutineScope,
@Background bgDispatcher: CoroutineDispatcher,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractor.kt
new file mode 100644
index 0000000..a51421c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractor.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.annotation.FloatRange
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.TransitionInfo
+import com.android.systemui.keyguard.shared.model.TransitionState
+import java.util.UUID
+import javax.inject.Inject
+import kotlinx.coroutines.flow.StateFlow
+
+/**
+ * This interactor provides direct access to [KeyguardTransitionRepository] internals and exposes
+ * functions to directly modify the transition state.
+ */
+@SysUISingleton
+class InternalKeyguardTransitionInteractor
+@Inject
+constructor(
+ private val repository: KeyguardTransitionRepository,
+) {
+
+ /**
+ * The [TransitionInfo] of the most recent call to
+ * [KeyguardTransitionRepository.startTransition].
+ *
+ * This should only be used by keyguard transition internals (From*TransitionInteractor and
+ * related classes). Other consumers of keyguard state in System UI should use
+ * [startedKeyguardState], [currentKeyguardState], and related flows.
+ *
+ * Keyguard internals use this to determine the most up-to-date KeyguardState that we've
+ * requested a transition to, even if the animator running the transition on the main thread has
+ * not yet emitted the STARTED TransitionStep.
+ *
+ * For example: if we're finished in GONE and press the power button twice very quickly, we may
+ * request a transition to AOD, but then receive the second power button press prior to the
+ * STARTED -> AOD transition step emitting. We still need the FromAodTransitionInteractor to
+ * request a transition from AOD -> LOCKSCREEN in response to the power press, even though the
+ * main thread animator hasn't emitted STARTED > AOD yet (which means [startedKeyguardState] is
+ * still GONE, which is not relevant to FromAodTransitionInteractor). In this case, the
+ * interactor can use this current transition info to determine that a STARTED -> AOD step
+ * *will* be emitted, and therefore that it can safely request an AOD -> LOCKSCREEN transition
+ * which will subsequently cancel GONE -> AOD.
+ */
+ internal val currentTransitionInfoInternal: StateFlow<TransitionInfo> =
+ repository.currentTransitionInfoInternal
+
+ suspend fun startTransition(info: TransitionInfo) = repository.startTransition(info)
+
+ fun updateTransition(
+ transitionId: UUID,
+ @FloatRange(from = 0.0, to = 1.0) value: Float,
+ state: TransitionState
+ ) = repository.updateTransition(transitionId, value, state)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
index 8d683f0..66efde1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
@@ -49,6 +49,7 @@
val repository: KeyguardRepository,
val biometricSettingsRepository: BiometricSettingsRepository,
transitionInteractor: KeyguardTransitionInteractor,
+ internalTransitionInteractor: InternalKeyguardTransitionInteractor,
) {
/**
@@ -74,7 +75,7 @@
// Whenever the keyguard is disabled...
.filter { enabled -> !enabled }
.sampleCombine(
- transitionInteractor.currentTransitionInfoInternal,
+ internalTransitionInteractor.currentTransitionInfoInternal,
biometricSettingsRepository.isCurrentUserInLockdown
)
.map { (_, transitionInfo, inLockdown) ->
@@ -93,7 +94,7 @@
.filter { enabled -> !enabled }
.sampleCombine(
biometricSettingsRepository.isCurrentUserInLockdown,
- transitionInteractor.currentTransitionInfoInternal,
+ internalTransitionInteractor.currentTransitionInfoInternal,
)
.collect { (_, inLockdown, currentTransitionInfo) ->
if (currentTransitionInfo.to != KeyguardState.GONE && !inLockdown) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 22ab82b..859326a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -23,9 +23,10 @@
import android.graphics.Point
import android.util.MathUtils
import com.android.app.animation.Interpolators
+import com.android.app.tracing.FlowTracing.tracedAwaitClose
+import com.android.app.tracing.FlowTracing.tracedConflatedCallbackFlow
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
@@ -51,13 +52,13 @@
import com.android.systemui.statusbar.notification.NotificationUtils.interpolate
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
+import com.android.systemui.util.kotlin.Utils.Companion.sampleFilter
import com.android.systemui.util.kotlin.pairwise
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import javax.inject.Provider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -197,22 +198,22 @@
val isActiveDreamLockscreenHosted: StateFlow<Boolean> = repository.isActiveDreamLockscreenHosted
/** Event for when the camera gesture is detected */
- val onCameraLaunchDetected: Flow<CameraLaunchSourceModel> = conflatedCallbackFlow {
- val callback =
- object : CommandQueue.Callbacks {
- override fun onCameraLaunchGestureDetected(source: Int) {
- trySendWithFailureLogging(
- cameraLaunchSourceIntToModel(source),
- TAG,
- "updated onCameraLaunchGestureDetected"
- )
+ val onCameraLaunchDetected: Flow<CameraLaunchSourceModel> =
+ tracedConflatedCallbackFlow("KeyguardInteractor#onCameraLaunchDetected") {
+ val callback =
+ object : CommandQueue.Callbacks {
+ override fun onCameraLaunchGestureDetected(source: Int) {
+ trySendWithFailureLogging(
+ cameraLaunchSourceIntToModel(source),
+ TAG,
+ "updated onCameraLaunchGestureDetected")
+ }
}
- }
- commandQueue.addCallback(callback)
+ commandQueue.addCallback(callback)
- awaitClose { commandQueue.removeCallback(callback) }
- }
+ tracedAwaitClose("onCameraLaunchDetected") { commandQueue.removeCallback(callback) }
+ }
/**
* Dozing and dreaming have overlapping events. If the doze state remains in FINISH, it means
@@ -250,17 +251,13 @@
/** Keyguard can be clipped at the top as the shade is dragged */
val topClippingBounds: Flow<Int?> by lazy {
- combineTransform(
+ repository.topClippingBounds
+ .sampleFilter(
keyguardTransitionInteractor
.transitionValue(scene = Scenes.Gone, stateWithoutSceneContainer = GONE)
- .map { it == 1f }
- .onStart { emit(false) }
- .distinctUntilChanged(),
- repository.topClippingBounds
- ) { isGone, topClippingBounds ->
- if (!isGone) {
- emit(topClippingBounds)
- }
+ .onStart { emit(0f) }
+ ) { goneValue ->
+ goneValue != 1f
}
.distinctUntilChanged()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt
index f385671..5af38ba 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt
@@ -24,6 +24,7 @@
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.util.kotlin.sample
import dagger.Lazy
import javax.inject.Inject
@@ -53,6 +54,7 @@
private val repository: KeyguardOcclusionRepository,
private val powerInteractor: PowerInteractor,
private val transitionInteractor: KeyguardTransitionInteractor,
+ private val internalTransitionInteractor: InternalKeyguardTransitionInteractor,
keyguardInteractor: KeyguardInteractor,
deviceUnlockedInteractor: Lazy<DeviceUnlockedInteractor>,
) {
@@ -78,7 +80,7 @@
// *_BOUNCER -> LOCKSCREEN.
return powerInteractor.detailedWakefulness.value.powerButtonLaunchGestureTriggered &&
KeyguardState.deviceIsAsleepInState(
- transitionInteractor.currentTransitionInfoInternal.value.to
+ internalTransitionInteractor.currentTransitionInfoInternal.value.to
)
}
@@ -93,10 +95,15 @@
// currently
// GONE, in which case we're going back to GONE and launching the insecure camera).
powerInteractor.detailedWakefulness
- .sample(transitionInteractor.currentKeyguardState, ::Pair)
- .map { (wakefulness, currentKeyguardState) ->
- wakefulness.powerButtonLaunchGestureTriggered &&
- currentKeyguardState != KeyguardState.GONE
+ .sample(
+ transitionInteractor.isFinishedIn(
+ Scenes.Gone,
+ stateWithoutSceneContainer = KeyguardState.GONE
+ ),
+ ::Pair
+ )
+ .map { (wakefulness, isOnGone) ->
+ wakefulness.powerButtonLaunchGestureTriggered && !isOnGone
},
// Emit false once that activity goes away.
isShowWhenLockedActivityOnTop.filter { !it }.map { false }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt
index 82255a0..bcbdc9c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt
@@ -19,9 +19,10 @@
import android.content.Context
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.KeyguardSurfaceBehindRepository
-import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor.Companion.isSurfaceVisible
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardSurfaceBehindModel
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor
import com.android.systemui.util.kotlin.sample
import com.android.systemui.util.kotlin.toPx
@@ -56,12 +57,18 @@
*/
val viewParams: Flow<KeyguardSurfaceBehindModel> =
combine(
- transitionInteractor.startedKeyguardTransitionStep,
- transitionInteractor.currentKeyguardState,
+ transitionInteractor.isInTransition(
+ Edge.create(to = Scenes.Gone),
+ edgeWithoutSceneContainer = Edge.create(to = KeyguardState.GONE)
+ ),
+ transitionInteractor.isFinishedIn(
+ Scenes.Gone,
+ stateWithoutSceneContainer = KeyguardState.GONE
+ ),
notificationLaunchInteractor.isLaunchAnimationRunning,
- ) { startedStep, currentState, notifAnimationRunning ->
+ ) { transitioningToGone, isOnGone, notifAnimationRunning ->
// If we're in transition to GONE, special unlock animation params apply.
- if (startedStep.to == KeyguardState.GONE && currentState != KeyguardState.GONE) {
+ if (transitioningToGone) {
if (notifAnimationRunning) {
// If the notification launch animation is running, leave the alpha at 0f.
// The ActivityLaunchAnimator will morph it from the notification at the
@@ -87,14 +94,14 @@
animateFromTranslationY =
SURFACE_TRANSLATION_Y_DISTANCE_DP.toPx(context).toFloat(),
translationY = 0f,
- startVelocity = swipeToDismissInteractor.dismissFling.value?.velocity
- ?: 0f,
+ startVelocity =
+ swipeToDismissInteractor.dismissFling.value?.velocity ?: 0f,
)
}
}
// Default to the visibility of the current state, with no animations.
- KeyguardSurfaceBehindModel(alpha = if (isSurfaceVisible(currentState)) 1f else 0f)
+ KeyguardSurfaceBehindModel(alpha = if (isOnGone) 1f else 0f)
}
.distinctUntilChanged()
@@ -103,10 +110,14 @@
*/
private val isNotificationLaunchAnimationRunningOnKeyguard =
notificationLaunchInteractor.isLaunchAnimationRunning
- .sample(transitionInteractor.finishedKeyguardState, ::Pair)
- .map { (animationRunning, finishedState) ->
- animationRunning && finishedState != KeyguardState.GONE
- }
+ .sample(
+ transitionInteractor.isFinishedIn(
+ Scenes.Gone,
+ stateWithoutSceneContainer = KeyguardState.GONE
+ ),
+ ::Pair
+ )
+ .map { (animationRunning, isOnGone) -> animationRunning && !isOnGone }
.onStart { emit(false) }
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt
index 5ad7762..805dbb0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt
@@ -22,7 +22,10 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.KeyguardState.OFF
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -40,6 +43,7 @@
val deviceEntryInteractor: DeviceEntryInteractor,
val deviceProvisioningInteractor: DeviceProvisioningInteractor,
val keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ val internalTransitionInteractor: InternalKeyguardTransitionInteractor,
val repository: KeyguardTransitionRepository,
) : CoreStartable {
@@ -56,17 +60,7 @@
override fun start() {
scope.launch {
- val state =
- if (showLockscreenOnBoot.first()) {
- KeyguardState.LOCKSCREEN
- } else {
- KeyguardState.GONE
- }
-
- if (
- keyguardTransitionInteractor.currentTransitionInfoInternal.value.from !=
- KeyguardState.OFF
- ) {
+ if (internalTransitionInteractor.currentTransitionInfoInternal.value.from != OFF) {
Log.e(
"KeyguardTransitionInteractor",
"showLockscreenOnBoot emitted, but we've already " +
@@ -74,7 +68,23 @@
"transition, but this should not happen."
)
} else {
- repository.emitInitialStepsFromOff(state)
+ if (SceneContainerFlag.isEnabled) {
+ // TODO(b/336576536): Some part of the transition implemented for flag off is
+ // missing here. There are two things achieved with this:
+ // 1. Keyguard is hidden when the setup wizard is shown. This part is already
+ // implemented in scene container by disabling visibility instead of going
+ // to Gone. See [SceneContainerStartable.hydrateVisibility]. We might want
+ // to unify this logic here.
+ // 2. When the auth method is set to NONE device boots into Gone (Launcher).
+ // For this we would just need to call changeScene(Scene.Gone).
+ // Unfortunately STL doesn't seem to be initialized at this point, therefore
+ // it needs a different solution.
+ repository.emitInitialStepsFromOff(LOCKSCREEN)
+ } else {
+ repository.emitInitialStepsFromOff(
+ if (showLockscreenOnBoot.first()) LOCKSCREEN else GONE
+ )
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index 2d389aa..f9bfaff 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard.domain.interactor
-import android.annotation.FloatRange
import android.annotation.SuppressLint
import android.util.Log
import com.android.compose.animation.scene.SceneKey
@@ -33,14 +32,12 @@
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.shared.model.KeyguardState.UNDEFINED
-import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.util.kotlin.pairwise
-import java.util.UUID
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -50,6 +47,7 @@
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
@@ -75,7 +73,7 @@
private val fromAlternateBouncerTransitionInteractor:
dagger.Lazy<FromAlternateBouncerTransitionInteractor>,
private val fromDozingTransitionInteractor: dagger.Lazy<FromDozingTransitionInteractor>,
- private val sceneInteractor: dagger.Lazy<SceneInteractor>,
+ private val sceneInteractor: SceneInteractor,
) {
private val transitionMap = mutableMapOf<Edge.StateToState, MutableSharedFlow<TransitionStep>>()
@@ -194,7 +192,7 @@
fun SceneKey?.isLockscreenOrNull() = this == Scenes.Lockscreen || this == null
return@filter (fromScene.isLockscreenOrNull() && toScene.isLockscreenOrNull()) ||
- sceneInteractor.get().transitionState.value.isTransitioning(fromScene, toScene)
+ sceneInteractor.transitionState.value.isTransitioning(fromScene, toScene)
}
} else {
flow
@@ -228,7 +226,7 @@
stateWithoutSceneContainer: KeyguardState,
): Flow<Float> {
return if (SceneContainerFlag.isEnabled) {
- sceneInteractor.get().transitionProgress(scene)
+ sceneInteractor.transitionProgress(scene)
} else {
transitionValue(stateWithoutSceneContainer)
}
@@ -383,33 +381,14 @@
.distinctUntilChanged()
.stateIn(scope, SharingStarted.Eagerly, KeyguardState.OFF)
- /**
- * The [TransitionInfo] of the most recent call to
- * [KeyguardTransitionRepository.startTransition].
- *
- * This should only be used by keyguard transition internals (From*TransitionInteractor and
- * related classes). Other consumers of keyguard state in System UI should use
- * [startedKeyguardState], [currentKeyguardState], and related flows.
- *
- * Keyguard internals use this to determine the most up-to-date KeyguardState that we've
- * requested a transition to, even if the animator running the transition on the main thread has
- * not yet emitted the STARTED TransitionStep.
- *
- * For example: if we're finished in GONE and press the power button twice very quickly, we may
- * request a transition to AOD, but then receive the second power button press prior to the
- * STARTED -> AOD transition step emitting. We still need the FromAodTransitionInteractor to
- * request a transition from AOD -> LOCKSCREEN in response to the power press, even though the
- * main thread animator hasn't emitted STARTED > AOD yet (which means [startedKeyguardState] is
- * still GONE, which is not relevant to FromAodTransitionInteractor). In this case, the
- * interactor can use this current transition info to determine that a STARTED -> AOD step
- * *will* be emitted, and therefore that it can safely request an AOD -> LOCKSCREEN transition
- * which will subsequently cancel GONE -> AOD.
- */
- internal val currentTransitionInfoInternal: StateFlow<TransitionInfo> =
- repository.currentTransitionInfoInternal
-
- /** Whether we've currently STARTED a transition and haven't yet FINISHED it. */
- val isInTransitionToAnyState = isInTransitionWhere({ true }, { true })
+ val isInTransition =
+ combine(
+ isInTransitionWhere({ true }, { true }),
+ sceneInteractor.transitionState,
+ ) { isKeyguardTransitioning, sceneTransitionState ->
+ isKeyguardTransitioning ||
+ (SceneContainerFlag.isEnabled && sceneTransitionState.isTransitioning())
+ }
/**
* Called to start a transition that will ultimately dismiss the keyguard from the current
@@ -422,7 +401,7 @@
// TODO(b/336576536): Check if adaptation for scene framework is needed
if (SceneContainerFlag.isEnabled) return
Log.d(TAG, "#startDismissKeyguardTransition(reason=$reason)")
- when (val startedState = currentTransitionInfoInternal.value.to) {
+ when (val startedState = repository.currentTransitionInfoInternal.value.to) {
LOCKSCREEN -> fromLockscreenTransitionInteractor.get().dismissKeyguard()
PRIMARY_BOUNCER -> fromPrimaryBouncerTransitionInteractor.get().dismissPrimaryBouncer()
ALTERNATE_BOUNCER ->
@@ -448,7 +427,7 @@
fun isInTransition(edge: Edge, edgeWithoutSceneContainer: Edge? = null): Flow<Boolean> {
return if (SceneContainerFlag.isEnabled) {
if (edge.isSceneWildcardEdge()) {
- sceneInteractor.get().transitionState.map {
+ sceneInteractor.transitionState.map {
when (edge) {
is Edge.StateToState ->
throw IllegalStateException("Should not be reachable.")
@@ -469,30 +448,6 @@
}
/**
- * Whether we're in a transition to a [KeyguardState] that matches the given predicate, but
- * haven't yet completed it.
- *
- * If you only care about a single state, instead use the optimized [isInTransition].
- */
- fun isInTransitionToStateWhere(
- stateMatcher: (KeyguardState) -> Boolean,
- ): Flow<Boolean> {
- return isInTransitionWhere(fromStatePredicate = { true }, toStatePredicate = stateMatcher)
- }
-
- /**
- * Whether we're in a transition out of a [KeyguardState] that matches the given predicate, but
- * haven't yet completed it.
- *
- * If you only care about a single state, instead use the optimized [isInTransition].
- */
- fun isInTransitionFromStateWhere(
- stateMatcher: (KeyguardState) -> Boolean,
- ): Flow<Boolean> {
- return isInTransitionWhere(fromStatePredicate = stateMatcher, toStatePredicate = { true })
- }
-
- /**
* Whether we're in a transition between two [KeyguardState]s that match the given predicates,
* but haven't yet completed it.
*
@@ -500,27 +455,15 @@
* [isInTransition].
*/
fun isInTransitionWhere(
- fromStatePredicate: (KeyguardState) -> Boolean,
- toStatePredicate: (KeyguardState) -> Boolean,
- ): Flow<Boolean> {
- return isInTransitionWhere { from, to -> fromStatePredicate(from) && toStatePredicate(to) }
- }
-
- /**
- * Whether we're in a transition between two [KeyguardState]s that match the given predicates,
- * but haven't yet completed it.
- *
- * If you only care about a single state for both from and to, instead use the optimized
- * [isInTransition].
- */
- private fun isInTransitionWhere(
- fromToStatePredicate: (KeyguardState, KeyguardState) -> Boolean
+ fromStatePredicate: (KeyguardState) -> Boolean = { true },
+ toStatePredicate: (KeyguardState) -> Boolean = { true },
): Flow<Boolean> {
return repository.transitions
.filter { it.transitionState != TransitionState.CANCELED }
.mapLatest {
it.transitionState != TransitionState.FINISHED &&
- fromToStatePredicate(it.from, it.to)
+ fromStatePredicate(it.from) &&
+ toStatePredicate(it.to)
}
.distinctUntilChanged()
}
@@ -532,9 +475,7 @@
fun isFinishedIn(scene: SceneKey, stateWithoutSceneContainer: KeyguardState): Flow<Boolean> {
return if (SceneContainerFlag.isEnabled) {
- sceneInteractor
- .get()
- .transitionState
+ sceneInteractor.transitionState
.map { it.isIdle(scene) || it.isTransitioning(from = scene) }
.distinctUntilChanged()
} else {
@@ -560,14 +501,6 @@
return finishedKeyguardState.replayCache.last()
}
- suspend fun startTransition(info: TransitionInfo) = repository.startTransition(info)
-
- fun updateTransition(
- transitionId: UUID,
- @FloatRange(from = 0.0, to = 1.0) value: Float,
- state: TransitionState
- ) = repository.updateTransition(transitionId, value, state)
-
companion object {
private val TAG = KeyguardTransitionInteractor::class.simpleName
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
index e148207..bbc3d76 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
@@ -25,6 +25,7 @@
import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.util.kotlin.sample
import java.util.UUID
import kotlinx.coroutines.CoroutineDispatcher
@@ -57,6 +58,8 @@
) {
val name = this::class.simpleName ?: "UnknownTransitionInteractor"
abstract val transitionRepository: KeyguardTransitionRepository
+ abstract val internalTransitionInteractor: InternalKeyguardTransitionInteractor
+
abstract fun start()
/* Use background dispatcher for all [KeyguardTransitionInteractor] flows. Necessary because
@@ -79,14 +82,15 @@
// a bugreport.
ownerReason: String = "",
): UUID? {
- if (fromState != transitionInteractor.currentTransitionInfoInternal.value.to) {
+ toState.checkValidState()
+ if (fromState != internalTransitionInteractor.currentTransitionInfoInternal.value.to) {
Log.e(
name,
"Ignoring startTransition: This interactor asked to transition from " +
"$fromState -> $toState, but we last transitioned to " +
- "${transitionInteractor.currentTransitionInfoInternal.value.to}, not " +
- "$fromState. This should never happen - check currentTransitionInfoInternal " +
- "or use filterRelevantKeyguardState before starting transitions."
+ "${internalTransitionInteractor.currentTransitionInfoInternal.value.to}, not" +
+ " $fromState. This should never happen - check currentTransitionInfoInternal" +
+ " or use filterRelevantKeyguardState before starting transitions."
)
if (fromState == transitionInteractor.finishedKeyguardState.replayCache.last()) {
@@ -164,6 +168,8 @@
*/
@Deprecated("Will be merged into maybeStartTransitionToOccludedOrInsecureCamera")
suspend fun maybeHandleInsecurePowerGesture(): Boolean {
+ // TODO(b/336576536): Check if adaptation for scene framework is needed
+ if (SceneContainerFlag.isEnabled) return true
if (keyguardOcclusionInteractor.shouldTransitionFromPowerButtonGesture()) {
if (keyguardInteractor.isKeyguardDismissible.value) {
startTransitionTo(
@@ -238,12 +244,11 @@
* Whether we're in the KeyguardState relevant to this From*TransitionInteractor (which we know
* from [fromState]).
*
- * This uses [KeyguardTransitionInteractor.currentTransitionInfoInternal], which is more up to
- * date than [startedKeyguardState] as it does not wait for the emission of the first STARTED
- * step.
+ * This uses [currentTransitionInfoInternal], which is more up to date than
+ * [startedKeyguardState] as it does not wait for the emission of the first STARTED step.
*/
fun inOrTransitioningToRelevantKeyguardState(): Boolean {
- return transitionInteractor.currentTransitionInfoInternal.value.to == fromState
+ return internalTransitionInteractor.currentTransitionInfoInternal.value.to == fromState
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index 069f65b..3355ffd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -129,7 +129,7 @@
}
}
} else {
- transitionInteractor.isInTransitionToAnyState.flatMapLatest { isInTransition ->
+ transitionInteractor.isInTransition.flatMapLatest { isInTransition ->
if (!isInTransition) {
defaultSurfaceBehindVisibility
} else {
@@ -206,11 +206,11 @@
transitionInteractor.currentKeyguardState
.sample(transitionInteractor.startedStepWithPrecedingStep, ::Pair)
.map { (currentState, startedWithPrev) ->
- val startedFromStep = startedWithPrev?.previousValue
- val startedStep = startedWithPrev?.newValue
+ val startedFromStep = startedWithPrev.previousValue
+ val startedStep = startedWithPrev.newValue
val returningToGoneAfterCancellation =
- startedStep?.to == KeyguardState.GONE &&
- startedFromStep?.transitionState == TransitionState.CANCELED &&
+ startedStep.to == KeyguardState.GONE &&
+ startedFromStep.transitionState == TransitionState.CANCELED &&
startedFromStep.from == KeyguardState.GONE
if (!returningToGoneAfterCancellation) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
index 9b3ba7d..3248114 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
@@ -23,6 +23,7 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.LockscreenSceneTransitionRepository
import com.android.systemui.keyguard.data.repository.LockscreenSceneTransitionRepository.Companion.DEFAULT_STATE
+import com.android.systemui.keyguard.domain.interactor.InternalKeyguardTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.UNDEFINED
@@ -67,7 +68,8 @@
class LockscreenSceneTransitionInteractor
@Inject
constructor(
- val transitionInteractor: KeyguardTransitionInteractor,
+ private val transitionInteractor: KeyguardTransitionInteractor,
+ private val internalTransitionInteractor: InternalKeyguardTransitionInteractor,
@Application private val applicationScope: CoroutineScope,
private val sceneInteractor: SceneInteractor,
private val repository: LockscreenSceneTransitionRepository,
@@ -123,7 +125,7 @@
}
private fun finishCurrentTransition() {
- transitionInteractor.updateTransition(currentTransitionId!!, 1f, FINISHED)
+ internalTransitionInteractor.updateTransition(currentTransitionId!!, 1f, FINISHED)
resetTransitionData()
}
@@ -131,13 +133,13 @@
val newTransition =
TransitionInfo(
ownerName = this::class.java.simpleName,
- from = transitionInteractor.currentTransitionInfoInternal.value.to,
+ from = internalTransitionInteractor.currentTransitionInfoInternal.value.to,
to = state,
animator = null,
modeOnCanceled = TransitionModeOnCanceled.REVERSE
)
- currentTransitionId = transitionInteractor.startTransition(newTransition)
- transitionInteractor.updateTransition(currentTransitionId!!, 1f, FINISHED)
+ currentTransitionId = internalTransitionInteractor.startTransition(newTransition)
+ internalTransitionInteractor.updateTransition(currentTransitionId!!, 1f, FINISHED)
resetTransitionData()
}
@@ -150,7 +152,8 @@
private suspend fun handleTransition(transition: ObservableTransitionState.Transition) {
if (transition.fromScene == Scenes.Lockscreen) {
if (currentTransitionId != null) {
- val currentToState = transitionInteractor.currentTransitionInfoInternal.value.to
+ val currentToState =
+ internalTransitionInteractor.currentTransitionInfoInternal.value.to
if (currentToState == UNDEFINED) {
transitionKtfTo(transitionInteractor.getStartedFromState())
}
@@ -201,7 +204,7 @@
}
private suspend fun startTransitionFromLockscreen() {
- val currentState = transitionInteractor.currentTransitionInfoInternal.value.to
+ val currentState = internalTransitionInteractor.currentTransitionInfoInternal.value.to
val newTransition =
TransitionInfo(
ownerName = this::class.java.simpleName,
@@ -217,12 +220,12 @@
if (currentTransitionId != null) {
resetTransitionData()
}
- currentTransitionId = transitionInteractor.startTransition(transitionInfo)
+ currentTransitionId = internalTransitionInteractor.startTransition(transitionInfo)
}
private fun updateProgress(progress: Float) {
if (currentTransitionId == null) return
- transitionInteractor.updateTransition(
+ internalTransitionInteractor.updateTransition(
currentTransitionId!!,
progress.coerceIn(0f, 1f),
RUNNING
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
index 1c7b4d9..76f7749 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
@@ -38,7 +38,9 @@
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.statusbar.VibratorHelper
+import com.android.systemui.util.kotlin.DisposableHandles
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
@@ -64,8 +66,9 @@
falsingManager: FalsingManager,
vibratorHelper: VibratorHelper,
overrideColor: Color? = null,
- ) {
+ ): DisposableHandle {
DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()
+ val disposables = DisposableHandles()
val longPressHandlingView = view.longPressHandlingView
val fgIconView = view.iconView
val bgView = view.bgView
@@ -83,118 +86,125 @@
}
}
- view.repeatWhenAttached {
- // Repeat on CREATED so that the view will always observe the entire
- // GONE => AOD transition (even though the view may not be visible until the middle
- // of the transition.
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- launch("$TAG#viewModel.isVisible") {
- viewModel.isVisible.collect { isVisible ->
- longPressHandlingView.isInvisible = !isVisible
+ disposables +=
+ view.repeatWhenAttached {
+ // Repeat on CREATED so that the view will always observe the entire
+ // GONE => AOD transition (even though the view may not be visible until the middle
+ // of the transition.
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ launch("$TAG#viewModel.isVisible") {
+ viewModel.isVisible.collect { isVisible ->
+ longPressHandlingView.isInvisible = !isVisible
+ }
}
- }
- launch("$TAG#viewModel.isLongPressEnabled") {
- viewModel.isLongPressEnabled.collect { isEnabled ->
- longPressHandlingView.setLongPressHandlingEnabled(isEnabled)
+ launch("$TAG#viewModel.isLongPressEnabled") {
+ viewModel.isLongPressEnabled.collect { isEnabled ->
+ longPressHandlingView.setLongPressHandlingEnabled(isEnabled)
+ }
}
- }
- launch("$TAG#viewModel.isUdfpsSupported") {
- viewModel.isUdfpsSupported.collect { udfpsSupported ->
- longPressHandlingView.longPressDuration =
- if (udfpsSupported) {
- {
- view.resources
- .getInteger(R.integer.config_udfpsDeviceEntryIconLongPress)
- .toLong()
+ launch("$TAG#viewModel.isUdfpsSupported") {
+ viewModel.isUdfpsSupported.collect { udfpsSupported ->
+ longPressHandlingView.longPressDuration =
+ if (udfpsSupported) {
+ {
+ view.resources
+ .getInteger(
+ R.integer.config_udfpsDeviceEntryIconLongPress
+ )
+ .toLong()
+ }
+ } else {
+ {
+ view.resources
+ .getInteger(R.integer.config_lockIconLongPress)
+ .toLong()
+ }
+ }
+ }
+ }
+ launch("$TAG#viewModel.accessibilityDelegateHint") {
+ viewModel.accessibilityDelegateHint.collect { hint ->
+ view.accessibilityHintType = hint
+ if (hint != DeviceEntryIconView.AccessibilityHintType.NONE) {
+ view.setOnClickListener {
+ vibratorHelper.performHapticFeedback(
+ view,
+ HapticFeedbackConstants.CONFIRM,
+ )
+ applicationScope.launch { viewModel.onUserInteraction() }
}
} else {
- {
- view.resources
- .getInteger(R.integer.config_lockIconLongPress)
- .toLong()
- }
+ view.setOnClickListener(null)
}
+ }
}
- }
- launch("$TAG#viewModel.accessibilityDelegateHint") {
- viewModel.accessibilityDelegateHint.collect { hint ->
- view.accessibilityHintType = hint
- if (hint != DeviceEntryIconView.AccessibilityHintType.NONE) {
- view.setOnClickListener {
- vibratorHelper.performHapticFeedback(
- view,
- HapticFeedbackConstants.CONFIRM,
- )
- applicationScope.launch { viewModel.onUserInteraction() }
+ launch("$TAG#viewModel.useBackgroundProtection") {
+ viewModel.useBackgroundProtection.collect { useBackgroundProtection ->
+ if (useBackgroundProtection) {
+ bgView.visibility = View.VISIBLE
+ } else {
+ bgView.visibility = View.GONE
}
- } else {
- view.setOnClickListener(null)
}
}
- }
- launch("$TAG#viewModel.useBackgroundProtection") {
- viewModel.useBackgroundProtection.collect { useBackgroundProtection ->
- if (useBackgroundProtection) {
- bgView.visibility = View.VISIBLE
- } else {
- bgView.visibility = View.GONE
+ launch("$TAG#viewModel.burnInOffsets") {
+ viewModel.burnInOffsets.collect { burnInOffsets ->
+ view.translationX = burnInOffsets.x.toFloat()
+ view.translationY = burnInOffsets.y.toFloat()
+ view.aodFpDrawable.progress = burnInOffsets.progress
}
}
- }
- launch("$TAG#viewModel.burnInOffsets") {
- viewModel.burnInOffsets.collect { burnInOffsets ->
- view.translationX = burnInOffsets.x.toFloat()
- view.translationY = burnInOffsets.y.toFloat()
- view.aodFpDrawable.progress = burnInOffsets.progress
- }
- }
- launch("$TAG#viewModel.deviceEntryViewAlpha") {
- viewModel.deviceEntryViewAlpha.collect { alpha -> view.alpha = alpha }
- }
- }
- }
-
- fgIconView.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- // Start with an empty state
- fgIconView.setImageState(StateSet.NOTHING, /* merge */ false)
- launch("$TAG#fpIconView.viewModel") {
- fgViewModel.viewModel.collect { viewModel ->
- fgIconView.setImageState(
- view.getIconState(viewModel.type, viewModel.useAodVariant),
- /* merge */ false
- )
- if (viewModel.type.contentDescriptionResId != -1) {
- fgIconView.contentDescription =
- fgIconView.resources.getString(
- viewModel.type.contentDescriptionResId
- )
- }
- fgIconView.imageTintList =
- ColorStateList.valueOf(overrideColor?.toArgb() ?: viewModel.tint)
- fgIconView.setPadding(
- viewModel.padding,
- viewModel.padding,
- viewModel.padding,
- viewModel.padding,
- )
+ launch("$TAG#viewModel.deviceEntryViewAlpha") {
+ viewModel.deviceEntryViewAlpha.collect { alpha -> view.alpha = alpha }
}
}
}
- }
- bgView.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- launch("$TAG#bgViewModel.alpha") {
- bgViewModel.alpha.collect { alpha -> bgView.alpha = alpha }
- }
- launch("$TAG#bgViewModel.color") {
- bgViewModel.color.collect { color ->
- bgView.imageTintList = ColorStateList.valueOf(color)
+ disposables +=
+ fgIconView.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ // Start with an empty state
+ fgIconView.setImageState(StateSet.NOTHING, /* merge */ false)
+ launch("$TAG#fpIconView.viewModel") {
+ fgViewModel.viewModel.collect { viewModel ->
+ fgIconView.setImageState(
+ view.getIconState(viewModel.type, viewModel.useAodVariant),
+ /* merge */ false
+ )
+ if (viewModel.type.contentDescriptionResId != -1) {
+ fgIconView.contentDescription =
+ fgIconView.resources.getString(
+ viewModel.type.contentDescriptionResId
+ )
+ }
+ fgIconView.imageTintList =
+ ColorStateList.valueOf(overrideColor?.toArgb() ?: viewModel.tint)
+ fgIconView.setPadding(
+ viewModel.padding,
+ viewModel.padding,
+ viewModel.padding,
+ viewModel.padding,
+ )
+ }
}
}
}
- }
+
+ disposables +=
+ bgView.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ launch("$TAG#bgViewModel.alpha") {
+ bgViewModel.alpha.collect { alpha -> bgView.alpha = alpha }
+ }
+ launch("$TAG#bgViewModel.color") {
+ bgViewModel.color.collect { color ->
+ bgView.imageTintList = ColorStateList.valueOf(color)
+ }
+ }
+ }
+ }
+
+ return disposables
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
index e063380..ba9f018 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
@@ -37,7 +37,9 @@
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.clocks.AodClockBurnInModel
import com.android.systemui.plugins.clocks.ClockController
+import com.android.systemui.util.kotlin.DisposableHandles
import com.android.systemui.util.ui.value
+import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
@@ -56,85 +58,94 @@
keyguardClockInteractor: KeyguardClockInteractor,
blueprintInteractor: KeyguardBlueprintInteractor,
rootViewModel: KeyguardRootViewModel,
- ) {
- keyguardRootView.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- keyguardClockInteractor.clockEventController.registerListeners(keyguardRootView)
+ ): DisposableHandle {
+ val disposables = DisposableHandles()
+ disposables +=
+ keyguardRootView.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ keyguardClockInteractor.clockEventController.registerListeners(keyguardRootView)
+ }
}
- }
- keyguardRootView.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- launch {
- if (!MigrateClocksToBlueprint.isEnabled) return@launch
- viewModel.currentClock.collect { currentClock ->
- cleanupClockViews(currentClock, keyguardRootView, viewModel.burnInLayer)
- addClockViews(currentClock, keyguardRootView)
- updateBurnInLayer(keyguardRootView, viewModel, viewModel.clockSize.value)
- applyConstraints(clockSection, keyguardRootView, true)
- }
- }
-
- launch {
- if (!MigrateClocksToBlueprint.isEnabled) return@launch
- viewModel.clockSize.collect { clockSize ->
- updateBurnInLayer(keyguardRootView, viewModel, clockSize)
- blueprintInteractor.refreshBlueprint(Type.ClockSize)
- }
- }
-
- launch {
- if (!MigrateClocksToBlueprint.isEnabled) return@launch
- viewModel.clockShouldBeCentered.collect {
- viewModel.currentClock.value?.let {
- // TODO(b/301502635): remove "!it.config.useCustomClockScene" when
- // migrate clocks to blueprint is fully rolled out
- if (
- it.largeClock.config.hasCustomPositionUpdatedAnimation &&
- !it.config.useCustomClockScene
- ) {
- blueprintInteractor.refreshBlueprint(Type.DefaultClockStepping)
- } else {
- blueprintInteractor.refreshBlueprint(Type.DefaultTransition)
- }
+ disposables +=
+ keyguardRootView.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ launch {
+ if (!MigrateClocksToBlueprint.isEnabled) return@launch
+ viewModel.currentClock.collect { currentClock ->
+ cleanupClockViews(currentClock, keyguardRootView, viewModel.burnInLayer)
+ addClockViews(currentClock, keyguardRootView)
+ updateBurnInLayer(
+ keyguardRootView,
+ viewModel,
+ viewModel.clockSize.value
+ )
+ applyConstraints(clockSection, keyguardRootView, true)
}
}
- }
- launch {
- if (!MigrateClocksToBlueprint.isEnabled) return@launch
- combine(
- viewModel.hasAodIcons,
- rootViewModel.isNotifIconContainerVisible.map { it.value }
- ) { hasIcon, isVisible ->
- hasIcon && isVisible
+ launch {
+ if (!MigrateClocksToBlueprint.isEnabled) return@launch
+ viewModel.clockSize.collect { clockSize ->
+ updateBurnInLayer(keyguardRootView, viewModel, clockSize)
+ blueprintInteractor.refreshBlueprint(Type.ClockSize)
}
- .distinctUntilChanged()
- .collect { _ ->
+ }
+
+ launch {
+ if (!MigrateClocksToBlueprint.isEnabled) return@launch
+ viewModel.clockShouldBeCentered.collect {
viewModel.currentClock.value?.let {
- if (it.config.useCustomClockScene) {
+ // TODO(b/301502635): remove "!it.config.useCustomClockScene" when
+ // migrate clocks to blueprint is fully rolled out
+ if (
+ it.largeClock.config.hasCustomPositionUpdatedAnimation &&
+ !it.config.useCustomClockScene
+ ) {
+ blueprintInteractor.refreshBlueprint(Type.DefaultClockStepping)
+ } else {
blueprintInteractor.refreshBlueprint(Type.DefaultTransition)
}
}
}
- }
+ }
- launch {
- if (!MigrateClocksToBlueprint.isEnabled) return@launch
- rootViewModel.burnInModel.collect { burnInModel ->
- viewModel.currentClock.value?.let {
- it.largeClock.layout.applyAodBurnIn(
- AodClockBurnInModel(
- translationX = burnInModel.translationX.toFloat(),
- translationY = burnInModel.translationY.toFloat(),
- scale = burnInModel.scale
+ launch {
+ if (!MigrateClocksToBlueprint.isEnabled) return@launch
+ combine(
+ viewModel.hasAodIcons,
+ rootViewModel.isNotifIconContainerVisible.map { it.value }
+ ) { hasIcon, isVisible ->
+ hasIcon && isVisible
+ }
+ .distinctUntilChanged()
+ .collect { _ ->
+ viewModel.currentClock.value?.let {
+ if (it.config.useCustomClockScene) {
+ blueprintInteractor.refreshBlueprint(Type.DefaultTransition)
+ }
+ }
+ }
+ }
+
+ launch {
+ if (!MigrateClocksToBlueprint.isEnabled) return@launch
+ rootViewModel.burnInModel.collect { burnInModel ->
+ viewModel.currentClock.value?.let {
+ it.largeClock.layout.applyAodBurnIn(
+ AodClockBurnInModel(
+ translationX = burnInModel.translationX.toFloat(),
+ translationY = burnInModel.translationY.toFloat(),
+ scale = burnInModel.scale
+ )
)
- )
+ }
}
}
}
}
- }
+
+ return disposables
}
@VisibleForTesting
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 fc92afe..8f149fb 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
@@ -267,6 +267,23 @@
}
}
+ launch {
+ blueprintViewModel.currentTransition.collect { currentTransition ->
+ // When blueprint/clock transitions end (null), make sure NSSL is in
+ // the right place
+ if (currentTransition == null) {
+ childViews[nsslPlaceholderId]?.let { notificationListPlaceholder
+ ->
+ viewModel.onNotificationContainerBoundsChanged(
+ notificationListPlaceholder.top.toFloat(),
+ notificationListPlaceholder.bottom.toFloat(),
+ animate = true,
+ )
+ }
+ }
+ }
+ }
+
if (NotificationIconContainerRefactor.isEnabled) {
launch {
val iconsAppearTranslationPx =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
index 191056c..8b74f5d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
@@ -31,6 +31,7 @@
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
import com.android.systemui.shared.R as sharedR
+import kotlinx.coroutines.DisposableHandle
object KeyguardSmartspaceViewBinder {
@JvmStatic
@@ -39,8 +40,8 @@
clockViewModel: KeyguardClockViewModel,
smartspaceViewModel: KeyguardSmartspaceViewModel,
blueprintInteractor: KeyguardBlueprintInteractor,
- ) {
- keyguardRootView.repeatWhenAttached {
+ ): DisposableHandle {
+ return keyguardRootView.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
launch("$TAG#clockViewModel.hasCustomWeatherDataDisplay") {
if (!MigrateClocksToBlueprint.isEnabled) return@launch
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
index 0637bba..91e48b5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
@@ -48,6 +48,7 @@
import com.android.systemui.util.ui.value
import dagger.Lazy
import javax.inject.Inject
+import kotlinx.coroutines.DisposableHandle
internal fun ConstraintSet.setVisibility(
views: Iterable<View>,
@@ -70,21 +71,24 @@
val blueprintInteractor: Lazy<KeyguardBlueprintInteractor>,
private val rootViewModel: KeyguardRootViewModel,
) : KeyguardSection() {
+ private var disposableHandle: DisposableHandle? = null
+
override fun addViews(constraintLayout: ConstraintLayout) {}
override fun bindData(constraintLayout: ConstraintLayout) {
if (!MigrateClocksToBlueprint.isEnabled) {
return
}
-
- KeyguardClockViewBinder.bind(
- this,
- constraintLayout,
- keyguardClockViewModel,
- clockInteractor,
- blueprintInteractor.get(),
- rootViewModel,
- )
+ disposableHandle?.dispose()
+ disposableHandle =
+ KeyguardClockViewBinder.bind(
+ this,
+ constraintLayout,
+ keyguardClockViewModel,
+ clockInteractor,
+ blueprintInteractor.get(),
+ rootViewModel,
+ )
}
override fun applyConstraints(constraintSet: ConstraintSet) {
@@ -97,7 +101,13 @@
}
}
- override fun removeViews(constraintLayout: ConstraintLayout) {}
+ override fun removeViews(constraintLayout: ConstraintLayout) {
+ if (!MigrateClocksToBlueprint.isEnabled) {
+ return
+ }
+
+ disposableHandle?.dispose()
+ }
private fun buildConstraints(
clock: ClockController,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
index e01f0a1..51230dd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
@@ -49,6 +49,7 @@
import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.ExperimentalCoroutinesApi
/** Includes the device entry icon. */
@@ -70,6 +71,7 @@
private val vibratorHelper: Lazy<VibratorHelper>,
) : KeyguardSection() {
private val deviceEntryIconViewId = R.id.device_entry_icon_view
+ private var disposableHandle: DisposableHandle? = null
override fun addViews(constraintLayout: ConstraintLayout) {
if (
@@ -97,15 +99,17 @@
override fun bindData(constraintLayout: ConstraintLayout) {
if (DeviceEntryUdfpsRefactor.isEnabled) {
constraintLayout.findViewById<DeviceEntryIconView?>(deviceEntryIconViewId)?.let {
- DeviceEntryIconViewBinder.bind(
- applicationScope,
- it,
- deviceEntryIconViewModel.get(),
- deviceEntryForegroundViewModel.get(),
- deviceEntryBackgroundViewModel.get(),
- falsingManager.get(),
- vibratorHelper.get(),
- )
+ disposableHandle?.dispose()
+ disposableHandle =
+ DeviceEntryIconViewBinder.bind(
+ applicationScope,
+ it,
+ deviceEntryIconViewModel.get(),
+ deviceEntryForegroundViewModel.get(),
+ deviceEntryBackgroundViewModel.get(),
+ falsingManager.get(),
+ vibratorHelper.get(),
+ )
}
} else {
constraintLayout.findViewById<LockIconView?>(R.id.lock_icon_view)?.let {
@@ -178,6 +182,7 @@
override fun removeViews(constraintLayout: ConstraintLayout) {
if (DeviceEntryUdfpsRefactor.isEnabled) {
constraintLayout.removeView(deviceEntryIconViewId)
+ disposableHandle?.dispose()
} else {
constraintLayout.removeView(R.id.lock_icon_view)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
index 8a751f0..55fc718 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
@@ -37,6 +37,7 @@
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
import dagger.Lazy
import javax.inject.Inject
+import kotlinx.coroutines.DisposableHandle
@SysUISingleton
open class SmartspaceSection
@@ -56,6 +57,7 @@
private var smartspaceVisibilityListener: OnGlobalLayoutListener? = null
private var pastVisibility: Int = -1
+ private var disposableHandle: DisposableHandle? = null
override fun onRebuildBegin() {
smartspaceController.suppressDisconnects = true
@@ -96,12 +98,14 @@
override fun bindData(constraintLayout: ConstraintLayout) {
if (!MigrateClocksToBlueprint.isEnabled) return
if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return
- KeyguardSmartspaceViewBinder.bind(
- constraintLayout,
- keyguardClockViewModel,
- keyguardSmartspaceViewModel,
- blueprintInteractor.get(),
- )
+ disposableHandle?.dispose()
+ disposableHandle =
+ KeyguardSmartspaceViewBinder.bind(
+ constraintLayout,
+ keyguardClockViewModel,
+ keyguardSmartspaceViewModel,
+ blueprintInteractor.get(),
+ )
}
override fun applyConstraints(constraintSet: ConstraintSet) {
@@ -188,6 +192,8 @@
}
smartspaceView?.viewTreeObserver?.removeOnGlobalLayoutListener(smartspaceVisibilityListener)
smartspaceVisibilityListener = null
+
+ disposableHandle?.dispose()
}
private fun updateVisibility(constraintSet: ConstraintSet) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
index c4383fc..680f966 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.ui.viewmodel
import androidx.annotation.VisibleForTesting
+import com.android.app.tracing.FlowTracing.traceEmissionCount
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
@@ -229,7 +230,7 @@
)
}
.distinctUntilChanged()
- }
+ }.traceEmissionCount({"QuickAfforcances#button${position.toSlotId()}"})
}
private fun KeyguardQuickAffordanceModel.toViewModel(
diff --git a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
index 988fe64..18a04ec 100644
--- a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
@@ -158,7 +158,7 @@
}
if (mp != null) {
if (DEBUG) {
- Log.d(mTag, "mPlayer.pause+release piid:" + player.getPlayerIId());
+ Log.d(mTag, "mp.pause+release piid:" + mp.getPlayerIId());
}
mp.pause();
try {
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
index de490a5..311cbfb 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt
@@ -96,12 +96,27 @@
logDebug { "lockScreenState:isConfigSelected=$isConfigSelected" }
logDebug { "lockScreenState:isDefaultNotesAppSet=$isDefaultNotesAppSet" }
+ val isCustomLockScreenShortcutEnabled =
+ context.resources.getBoolean(R.bool.custom_lockscreen_shortcuts_enabled)
+ val isShortcutSelectedOrDefaultEnabled =
+ if (isCustomLockScreenShortcutEnabled) {
+ isConfigSelected
+ } else {
+ isStylusEverUsed
+ }
+ logDebug {
+ "lockScreenState:isCustomLockScreenShortcutEnabled=" +
+ isCustomLockScreenShortcutEnabled
+ }
+ logDebug {
+ "lockScreenState:isShortcutSelectedOrDefaultEnabled=" +
+ isShortcutSelectedOrDefaultEnabled
+ }
if (
isEnabled &&
isUserUnlocked &&
isDefaultNotesAppSet &&
- isConfigSelected &&
- isStylusEverUsed
+ isShortcutSelectedOrDefaultEnabled
) {
val contentDescription = ContentDescription.Resource(pickerNameResourceId)
val icon = Icon.Resource(pickerIconResourceId, contentDescription)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
index c0371b9..9b4d10f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
@@ -47,7 +47,7 @@
TileLazyGrid(modifier = modifier, columns = GridCells.Fixed(columns)) {
items(
tiles.size,
- key = { index -> sizedTiles[index].tile.spec },
+ key = { index -> sizedTiles[index].tile.spec.spec },
span = { index -> GridItemSpan(sizedTiles[index].width) }
) { index ->
Tile(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
index 19b8c66..62bfc72 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
@@ -27,6 +27,8 @@
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor.Companion.POSITION_AT_END
import com.android.systemui.qs.pipeline.domain.interactor.MinimumTilesInteractor
import com.android.systemui.qs.pipeline.shared.TileSpec
+import javax.inject.Inject
+import javax.inject.Named
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
@@ -37,22 +39,20 @@
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
-import javax.inject.Inject
-import javax.inject.Named
@SysUISingleton
@OptIn(ExperimentalCoroutinesApi::class)
class EditModeViewModel
@Inject
constructor(
- private val editTilesListInteractor: EditTilesListInteractor,
- private val currentTilesInteractor: CurrentTilesInteractor,
- private val tilesAvailabilityInteractor: TilesAvailabilityInteractor,
- private val minTilesInteractor: MinimumTilesInteractor,
- @Named("Default") private val defaultGridLayout: GridLayout,
- @Application private val applicationScope: CoroutineScope,
- gridLayoutTypeInteractor: GridLayoutTypeInteractor,
- gridLayoutMap: Map<GridLayoutType, @JvmSuppressWildcards GridLayout>,
+ private val editTilesListInteractor: EditTilesListInteractor,
+ private val currentTilesInteractor: CurrentTilesInteractor,
+ private val tilesAvailabilityInteractor: TilesAvailabilityInteractor,
+ private val minTilesInteractor: MinimumTilesInteractor,
+ @Named("Default") private val defaultGridLayout: GridLayout,
+ @Application private val applicationScope: CoroutineScope,
+ gridLayoutTypeInteractor: GridLayoutTypeInteractor,
+ gridLayoutMap: Map<GridLayoutType, @JvmSuppressWildcards GridLayout>,
) {
private val _isEditing = MutableStateFlow(false)
@@ -93,10 +93,12 @@
val editTilesData = editTilesListInteractor.getTilesToEdit()
// Query only the non current platform tiles, as any current tile is clearly
// available
- val unavailable = tilesAvailabilityInteractor.getUnavailableTiles(
- editTilesData.stockTiles.map { it.tileSpec }
- .minus(currentTilesInteractor.currentTilesSpecs.toSet())
- )
+ val unavailable =
+ tilesAvailabilityInteractor.getUnavailableTiles(
+ editTilesData.stockTiles
+ .map { it.tileSpec }
+ .minus(currentTilesInteractor.currentTilesSpecs.toSet())
+ )
currentTilesInteractor.currentTiles.map { tiles ->
val currentSpecs = tiles.map { it.spec }
val canRemoveTiles = currentSpecs.size > minimumTiles
@@ -106,28 +108,28 @@
val nonCurrentTiles = allTiles.filter { it.tileSpec !in currentSpecs }
(currentTiles + nonCurrentTiles)
- .filterNot { it.tileSpec in unavailable }
- .map {
- val current = it.tileSpec in currentSpecs
- val availableActions = buildSet {
- if (current) {
- add(AvailableEditActions.MOVE)
- if (canRemoveTiles) {
- add(AvailableEditActions.REMOVE)
- }
- } else {
- add(AvailableEditActions.ADD)
+ .filterNot { it.tileSpec in unavailable }
+ .map {
+ val current = it.tileSpec in currentSpecs
+ val availableActions = buildSet {
+ if (current) {
+ add(AvailableEditActions.MOVE)
+ if (canRemoveTiles) {
+ add(AvailableEditActions.REMOVE)
}
+ } else {
+ add(AvailableEditActions.ADD)
}
- EditTileViewModel(
- it.tileSpec,
- it.icon,
- it.label,
- it.appName,
- current,
- availableActions
- )
}
+ EditTileViewModel(
+ it.tileSpec,
+ it.icon,
+ it.label,
+ it.appName,
+ current,
+ availableActions
+ )
+ }
}
} else {
emptyFlow()
@@ -144,13 +146,16 @@
_isEditing.value = false
}
- /** Immediately moves [tileSpec] to [position]. */
- fun moveTile(tileSpec: TileSpec, position: Int) {
- throw NotImplementedError("This is not supported yet")
- }
-
- /** Immediately adds [tileSpec] to the current tiles at [position]. */
+ /**
+ * Immediately adds [tileSpec] to the current tiles at [position]. If the [tileSpec] was already
+ * present, it will be moved to the new position.
+ */
fun addTile(tileSpec: TileSpec, position: Int = POSITION_AT_END) {
+ // Removing tile if it's already present to insert it at the new index.
+ if (currentTilesInteractor.currentTilesSpecs.contains(tileSpec)) {
+ removeTile(tileSpec)
+ }
+
currentTilesInteractor.addTile(tileSpec, position)
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
index ef01194..5885193 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
@@ -28,6 +28,7 @@
import com.android.systemui.scene.shared.logger.SceneLogger
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.util.kotlin.getValue
import com.android.systemui.util.kotlin.pairwiseBy
import dagger.Lazy
import javax.inject.Inject
@@ -60,8 +61,8 @@
private val repository: SceneContainerRepository,
private val logger: SceneLogger,
private val sceneFamilyResolvers: Lazy<Map<SceneKey, @JvmSuppressWildcards SceneResolver>>,
- private val deviceUnlockedInteractor: DeviceUnlockedInteractor,
- private val keyguardEnabledInteractor: KeyguardEnabledInteractor,
+ private val deviceUnlockedInteractor: Lazy<DeviceUnlockedInteractor>,
+ private val keyguardEnabledInteractor: Lazy<KeyguardEnabledInteractor>,
) {
interface OnSceneAboutToChangeListener {
@@ -387,8 +388,8 @@
val isChangeAllowed =
to != Scenes.Gone ||
inMidTransitionFromGone ||
- deviceUnlockedInteractor.deviceUnlockStatus.value.isUnlocked ||
- !keyguardEnabledInteractor.isKeyguardEnabled.value
+ deviceUnlockedInteractor.get().deviceUnlockStatus.value.isUnlocked ||
+ !keyguardEnabledInteractor.get().isKeyguardEnabled.value
check(isChangeAllowed) {
"Cannot change to the Gone scene while the device is locked and not currently" +
" transitioning from Gone. Current transition state is ${transitionState.value}." +
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractor.kt
index ca73081..ef96f43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractor.kt
@@ -23,6 +23,7 @@
import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -90,17 +91,22 @@
/** Occlusion state to apply whenever a keyguard transition is FINISHED. */
private val occlusionStateFromFinishedStep =
- keyguardTransitionInteractor.finishedKeyguardTransitionStep
- .sample(keyguardOcclusionInteractor.isShowWhenLockedActivityOnTop, ::Pair)
- .map { (finishedStep, showWhenLockedOnTop) ->
+ combine(
+ keyguardTransitionInteractor.isFinishedIn(
+ Scenes.Gone,
+ stateWithoutSceneContainer = KeyguardState.GONE
+ ),
+ keyguardTransitionInteractor.isFinishedIn(KeyguardState.OCCLUDED),
+ keyguardOcclusionInteractor.isShowWhenLockedActivityOnTop,
+ ::Triple
+ )
+ .map { (isOnGone, isOnOccluded, showWhenLockedOnTop) ->
// If we're FINISHED in OCCLUDED, we want to render as occluded. We also need to
// remain occluded if a SHOW_WHEN_LOCKED activity is on the top of the task stack,
// and we're in any state other than GONE. This is necessary, for example, when we
// transition from OCCLUDED to a bouncer state. Otherwise, we should not be
// occluded.
- val occluded =
- finishedStep.to == KeyguardState.OCCLUDED ||
- (showWhenLockedOnTop && finishedStep.to != KeyguardState.GONE)
+ val occluded = isOnOccluded || (showWhenLockedOnTop && !isOnGone)
OccludedState(occluded = occluded, animate = false)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
index f62b24a..3dcaff3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
@@ -21,6 +21,7 @@
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_USE_PEOPLE_FILTERING
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.collection.NotificationClassificationFlag
import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype
import com.android.systemui.statusbar.notification.shared.PriorityPeopleSection
import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING
@@ -51,7 +52,8 @@
}
fun getNotificationBuckets(): IntArray {
- if (PriorityPeopleSection.isEnabled || NotificationMinimalismPrototype.V2.isEnabled) {
+ if (PriorityPeopleSection.isEnabled || NotificationMinimalismPrototype.V2.isEnabled
+ || NotificationClassificationFlag.isEnabled) {
// We don't need this list to be adaptive, it can be the superset of all features.
return PriorityBucket.getAllInOrder()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationClassificationFlag.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationClassificationFlag.kt
new file mode 100644
index 0000000..139347c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationClassificationFlag.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification.collection
+
+import android.service.notification.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/**
+ * Helper for android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION
+ */
+@Suppress("NOTHING_TO_INLINE")
+object NotificationClassificationFlag {
+ const val FLAG_NAME = Flags.FLAG_NOTIFICATION_CLASSIFICATION
+
+ /** A token used for dependency declaration */
+ val token: FlagToken
+ get() = FlagToken(FLAG_NAME, isEnabled)
+
+ /** Are sections sorted by time? */
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.notificationClassification()
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This protects users from the
+ * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+ * build to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun isUnexpectedlyInLegacyMode() =
+ RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is enabled to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt
new file mode 100644
index 0000000..244c594
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator
+
+import android.app.NotificationChannel.NEWS_ID
+import android.app.NotificationChannel.PROMOTIONS_ID
+import android.app.NotificationChannel.RECS_ID
+import android.app.NotificationChannel.SOCIAL_MEDIA_ID
+import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
+import com.android.systemui.statusbar.notification.collection.render.NodeController
+import com.android.systemui.statusbar.notification.dagger.NewsHeader
+import com.android.systemui.statusbar.notification.dagger.PromoHeader
+import com.android.systemui.statusbar.notification.dagger.RecsHeader
+import com.android.systemui.statusbar.notification.dagger.SocialHeader
+import com.android.systemui.statusbar.notification.stack.BUCKET_NEWS
+import com.android.systemui.statusbar.notification.stack.BUCKET_PROMO
+import com.android.systemui.statusbar.notification.stack.BUCKET_RECS
+import com.android.systemui.statusbar.notification.stack.BUCKET_SOCIAL
+import javax.inject.Inject
+
+/**
+ * Coordinator for sections derived from NotificationAssistantService classification.
+ */
+@CoordinatorScope
+class BundleCoordinator @Inject constructor(
+ @NewsHeader private val newsHeaderController: NodeController,
+ @SocialHeader private val socialHeaderController: NodeController,
+ @RecsHeader private val recsHeaderController: NodeController,
+ @PromoHeader private val promoHeaderController: NodeController,
+) : Coordinator {
+
+ val newsSectioner =
+ object : NotifSectioner("News", BUCKET_NEWS) {
+ override fun isInSection(entry: ListEntry): Boolean {
+ return entry.representativeEntry?.channel?.id == NEWS_ID
+ }
+
+ override fun getHeaderNodeController(): NodeController? {
+ return newsHeaderController
+ }
+ }
+
+ val socialSectioner =
+ object : NotifSectioner("Social", BUCKET_SOCIAL) {
+ override fun isInSection(entry: ListEntry): Boolean {
+ return entry.representativeEntry?.channel?.id == SOCIAL_MEDIA_ID
+ }
+
+ override fun getHeaderNodeController(): NodeController? {
+ return socialHeaderController
+ }
+ }
+
+ val recsSectioner =
+ object : NotifSectioner("Recommendations", BUCKET_RECS) {
+ override fun isInSection(entry: ListEntry): Boolean {
+ return entry.representativeEntry?.channel?.id == RECS_ID
+ }
+
+ override fun getHeaderNodeController(): NodeController? {
+ return recsHeaderController
+ }
+ }
+
+ val promoSectioner =
+ object : NotifSectioner("Promotions", BUCKET_PROMO) {
+ override fun isInSection(entry: ListEntry): Boolean {
+ return entry.representativeEntry?.channel?.id == PROMOTIONS_ID
+ }
+
+ override fun getHeaderNodeController(): NodeController? {
+ return promoHeaderController
+ }
+ }
+
+ override fun attach(pipeline: NotifPipeline) {
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
index e413522..e038982 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
@@ -17,10 +17,7 @@
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED
-import com.android.systemui.statusbar.notification.collection.NotifPipeline
-import com.android.systemui.statusbar.notification.collection.PipelineDumpable
-import com.android.systemui.statusbar.notification.collection.PipelineDumper
-import com.android.systemui.statusbar.notification.collection.SortBySectionTimeFlag
+import com.android.systemui.statusbar.notification.collection.*
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider
@@ -69,6 +66,7 @@
dismissibilityCoordinator: DismissibilityCoordinator,
dreamCoordinator: DreamCoordinator,
statsLoggerCoordinator: NotificationStatsLoggerCoordinator,
+ bundleCoordinator: BundleCoordinator,
) : NotifCoordinators {
private val mCoreCoordinators: MutableList<CoreCoordinator> = ArrayList()
@@ -132,6 +130,12 @@
mOrderedSections.add(conversationCoordinator.peopleSilentSectioner) // People Silent
}
mOrderedSections.add(rankingCoordinator.alertingSectioner) // Alerting
+ if (NotificationClassificationFlag.isEnabled) {
+ mOrderedSections.add(bundleCoordinator.newsSectioner);
+ mOrderedSections.add(bundleCoordinator.socialSectioner);
+ mOrderedSections.add(bundleCoordinator.recsSectioner);
+ mOrderedSections.add(bundleCoordinator.promoSectioner);
+ }
mOrderedSections.add(rankingCoordinator.silentSectioner) // Silent
mOrderedSections.add(rankingCoordinator.minimizedSectioner) // Minimized
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationSectionHeadersModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationSectionHeadersModule.kt
index ca43591..e661090 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationSectionHeadersModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationSectionHeadersModule.kt
@@ -80,6 +80,50 @@
.build()
@Provides
+ @NewsHeader
+ @SysUISingleton
+ @JvmStatic fun providesNewsHeaderSubcomponent(
+ builder: Provider<SectionHeaderControllerSubcomponent.Builder>
+ ) = builder.get()
+ .nodeLabel("news header")
+ .headerText(com.android.internal.R.string.news_notification_channel_label)
+ .clickIntentAction(Settings.ACTION_NOTIFICATION_SETTINGS)
+ .build()
+
+ @Provides
+ @SocialHeader
+ @SysUISingleton
+ @JvmStatic fun providesSocialHeaderSubcomponent(
+ builder: Provider<SectionHeaderControllerSubcomponent.Builder>
+ ) = builder.get()
+ .nodeLabel("social header")
+ .headerText(com.android.internal.R.string.social_notification_channel_label)
+ .clickIntentAction(Settings.ACTION_NOTIFICATION_SETTINGS)
+ .build()
+
+ @Provides
+ @RecsHeader
+ @SysUISingleton
+ @JvmStatic fun providesRecsHeaderSubcomponent(
+ builder: Provider<SectionHeaderControllerSubcomponent.Builder>
+ ) = builder.get()
+ .nodeLabel("recs header")
+ .headerText(com.android.internal.R.string.recs_notification_channel_label)
+ .clickIntentAction(Settings.ACTION_NOTIFICATION_SETTINGS)
+ .build()
+
+ @Provides
+ @PromoHeader
+ @SysUISingleton
+ @JvmStatic fun providesPromoHeaderSubcomponent(
+ builder: Provider<SectionHeaderControllerSubcomponent.Builder>
+ ) = builder.get()
+ .nodeLabel("promo header")
+ .headerText(com.android.internal.R.string.promotional_notification_channel_label)
+ .clickIntentAction(Settings.ACTION_NOTIFICATION_SETTINGS)
+ .build()
+
+ @Provides
@SilentHeader
@JvmStatic fun providesSilentHeaderNodeController(
@SilentHeader subcomponent: SectionHeaderControllerSubcomponent
@@ -126,6 +170,54 @@
@JvmStatic fun providesIncomingHeaderController(
@IncomingHeader subcomponent: SectionHeaderControllerSubcomponent
) = subcomponent.headerController
+
+ @Provides
+ @NewsHeader
+ @JvmStatic fun providesNewsHeaderNodeController(
+ @NewsHeader subcomponent: SectionHeaderControllerSubcomponent
+ ) = subcomponent.nodeController
+
+ @Provides
+ @NewsHeader
+ @JvmStatic fun providesNewsHeaderController(
+ @NewsHeader subcomponent: SectionHeaderControllerSubcomponent
+ ) = subcomponent.headerController
+
+ @Provides
+ @SocialHeader
+ @JvmStatic fun providesSocialHeaderNodeController(
+ @SocialHeader subcomponent: SectionHeaderControllerSubcomponent
+ ) = subcomponent.nodeController
+
+ @Provides
+ @SocialHeader
+ @JvmStatic fun providesSocialHeaderController(
+ @SocialHeader subcomponent: SectionHeaderControllerSubcomponent
+ ) = subcomponent.headerController
+
+ @Provides
+ @RecsHeader
+ @JvmStatic fun providesRecsHeaderNodeController(
+ @RecsHeader subcomponent: SectionHeaderControllerSubcomponent
+ ) = subcomponent.nodeController
+
+ @Provides
+ @RecsHeader
+ @JvmStatic fun providesRecsHeaderController(
+ @RecsHeader subcomponent: SectionHeaderControllerSubcomponent
+ ) = subcomponent.headerController
+
+ @Provides
+ @PromoHeader
+ @JvmStatic fun providesPromoHeaderNodeController(
+ @PromoHeader subcomponent: SectionHeaderControllerSubcomponent
+ ) = subcomponent.nodeController
+
+ @Provides
+ @PromoHeader
+ @JvmStatic fun providesPromoHeaderController(
+ @PromoHeader subcomponent: SectionHeaderControllerSubcomponent
+ ) = subcomponent.headerController
}
@Subcomponent(modules = [ SectionHeaderBindingModule::class ])
@@ -183,3 +275,19 @@
@Scope
@Retention(AnnotationRetention.BINARY)
annotation class SectionHeaderScope
+
+@Qualifier
+@Retention(AnnotationRetention.BINARY)
+annotation class NewsHeader
+
+@Qualifier
+@Retention(AnnotationRetention.BINARY)
+annotation class SocialHeader
+
+@Qualifier
+@Retention(AnnotationRetention.BINARY)
+annotation class RecsHeader
+
+@Qualifier
+@Retention(AnnotationRetention.BINARY)
+annotation class PromoHeader
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java
index 9e0dd8fc..1755123 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java
@@ -20,9 +20,13 @@
import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_FOREGROUND_SERVICE;
import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_HEADS_UP;
import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_MEDIA_CONTROLS;
+import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_NEWS;
import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_PEOPLE;
import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_PRIORITY_PEOPLE;
+import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_PROMO;
+import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_RECS;
import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_SILENT;
+import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_SOCIAL;
import android.annotation.Nullable;
import android.service.notification.StatusBarNotification;
@@ -135,6 +139,10 @@
return Notifications.Notification.SECTION_PEOPLE;
case BUCKET_ALERTING: return Notifications.Notification.SECTION_ALERTING;
case BUCKET_SILENT: return Notifications.Notification.SECTION_SILENT;
+ case BUCKET_NEWS: return Notifications.Notification.SECTION_NEWS;
+ case BUCKET_SOCIAL: return Notifications.Notification.SECTION_SOCIAL;
+ case BUCKET_RECS: return Notifications.Notification.SECTION_RECS;
+ case BUCKET_PROMO: return Notifications.Notification.SECTION_PROMO;
}
return Notifications.Notification.SECTION_UNKNOWN;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/Notifications.proto b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/Notifications.proto
index c2ab275..ce4356a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/Notifications.proto
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/Notifications.proto
@@ -43,6 +43,13 @@
SECTION_ALERTING = 4;
SECTION_SILENT = 5;
SECTION_FOREGROUND_SERVICE = 6;
+ SECTION_PRIORITY_PEOPLE = 7;
+ SECTION_TOP_ONGOING = 8;
+ SECTION_TOP_UNSEEN = 9;
+ SECTION_NEWS = 10;
+ SECTION_SOCIAL = 11;
+ SECTION_RECS = 12;
+ SECTION_PROMO = 13;
}
optional NotificationSection section = 6;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationPriorityBucket.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationPriorityBucket.kt
index fabb696..f4a4527 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationPriorityBucket.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationPriorityBucket.kt
@@ -20,6 +20,10 @@
BUCKET_PRIORITY_PEOPLE,
BUCKET_PEOPLE,
BUCKET_ALERTING,
+ BUCKET_NEWS,
+ BUCKET_SOCIAL,
+ BUCKET_RECS,
+ BUCKET_PROMO,
BUCKET_SILENT
]
)
@@ -35,6 +39,10 @@
BUCKET_PRIORITY_PEOPLE,
BUCKET_PEOPLE,
BUCKET_ALERTING,
+ BUCKET_NEWS,
+ BUCKET_SOCIAL,
+ BUCKET_RECS,
+ BUCKET_PROMO,
BUCKET_SILENT,
)
}
@@ -49,4 +57,9 @@
const val BUCKET_PRIORITY_PEOPLE = 7
const val BUCKET_PEOPLE = 4
const val BUCKET_ALERTING = 5
+const val BUCKET_NEWS = 10
+const val BUCKET_SOCIAL = 11
+const val BUCKET_RECS = 12
+const val BUCKET_PROMO = 13
const val BUCKET_SILENT = 6
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index 3400ad1..7441c70 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -22,12 +22,10 @@
import com.android.systemui.media.controls.ui.controller.KeyguardMediaController
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.SourceType
+import com.android.systemui.statusbar.notification.collection.NotificationClassificationFlag
import com.android.systemui.statusbar.notification.collection.render.MediaContainerController
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController
-import com.android.systemui.statusbar.notification.dagger.AlertingHeader
-import com.android.systemui.statusbar.notification.dagger.IncomingHeader
-import com.android.systemui.statusbar.notification.dagger.PeopleHeader
-import com.android.systemui.statusbar.notification.dagger.SilentHeader
+import com.android.systemui.statusbar.notification.dagger.*
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.SectionProvider
@@ -51,7 +49,11 @@
@IncomingHeader private val incomingHeaderController: SectionHeaderController,
@PeopleHeader private val peopleHeaderController: SectionHeaderController,
@AlertingHeader private val alertingHeaderController: SectionHeaderController,
- @SilentHeader private val silentHeaderController: SectionHeaderController
+ @SilentHeader private val silentHeaderController: SectionHeaderController,
+ @NewsHeader private val newsHeaderController: SectionHeaderController,
+ @SocialHeader private val socialHeaderController: SectionHeaderController,
+ @RecsHeader private val recsHeaderController: SectionHeaderController,
+ @PromoHeader private val promoHeaderController: SectionHeaderController
) : SectionProvider {
private val configurationListener =
@@ -84,6 +86,22 @@
val mediaControlsView: MediaContainerView?
get() = mediaContainerController.mediaContainerView
+ @VisibleForTesting
+ val newsHeaderView: SectionHeaderView?
+ get() = newsHeaderController.headerView
+
+ @VisibleForTesting
+ val socialHeaderView: SectionHeaderView?
+ get() = socialHeaderController.headerView
+
+ @VisibleForTesting
+ val recsHeaderView: SectionHeaderView?
+ get() = recsHeaderController.headerView
+
+ @VisibleForTesting
+ val promoHeaderView: SectionHeaderView?
+ get() = promoHeaderController.headerView
+
/** Must be called before use. */
fun initialize(parent: NotificationStackScrollLayout) {
check(!initialized) { "NotificationSectionsManager already initialized" }
@@ -107,15 +125,24 @@
incomingHeaderController.reinflateView(parent)
mediaContainerController.reinflateView(parent)
keyguardMediaController.attachSinglePaneContainer(mediaControlsView)
+ if (NotificationClassificationFlag.isEnabled) {
+ newsHeaderController.reinflateView(parent)
+ socialHeaderController.reinflateView(parent)
+ recsHeaderController.reinflateView(parent)
+ promoHeaderController.reinflateView(parent)
+ }
}
override fun beginsSection(view: View, previous: View?): Boolean =
view === silentHeaderView ||
- view === mediaControlsView ||
- view === peopleHeaderView ||
- view === alertingHeaderView ||
- view === incomingHeaderView ||
- getBucket(view) != getBucket(previous)
+ view === mediaControlsView ||
+ view === peopleHeaderView ||
+ view === alertingHeaderView ||
+ view === incomingHeaderView ||
+ (NotificationClassificationFlag.isEnabled && (view === newsHeaderView
+ || view === socialHeaderView || view === recsHeaderView
+ || view === promoHeaderView)) ||
+ getBucket(view) != getBucket(previous)
private fun getBucket(view: View?): Int? =
when {
@@ -124,6 +151,10 @@
view === mediaControlsView -> BUCKET_MEDIA_CONTROLS
view === peopleHeaderView -> BUCKET_PEOPLE
view === alertingHeaderView -> BUCKET_ALERTING
+ view === newsHeaderView -> BUCKET_NEWS
+ view === socialHeaderView -> BUCKET_SOCIAL
+ view === recsHeaderView -> BUCKET_RECS
+ view === promoHeaderView -> BUCKET_PROMO
view is ExpandableNotificationRow -> view.entry.bucket
else -> null
}
@@ -255,6 +286,12 @@
peopleHeaderView?.setForegroundColors(onSurface, onSurfaceVariant)
silentHeaderView?.setForegroundColors(onSurface, onSurfaceVariant)
alertingHeaderView?.setForegroundColors(onSurface, onSurfaceVariant)
+ if (NotificationClassificationFlag.isEnabled) {
+ newsHeaderView?.setForegroundColors(onSurface, onSurfaceVariant)
+ socialHeaderView?.setForegroundColors(onSurface, onSurfaceVariant)
+ recsHeaderView?.setForegroundColors(onSurface, onSurfaceVariant)
+ promoHeaderView?.setForegroundColors(onSurface, onSurfaceVariant)
+ }
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index cb03334..dab3799 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -2443,8 +2443,9 @@
return count;
}
- private void updateContentHeight() {
- final float scrimTopPadding = mAmbientState.isOnKeyguard() ? 0 : mMinimumPaddings;
+ @VisibleForTesting
+ void updateContentHeight() {
+ final float scrimTopPadding = getScrimTopPaddingOrZero();
final int shelfIntrinsicHeight = mShelf != null ? mShelf.getIntrinsicHeight() : 0;
final int footerIntrinsicHeight = mFooterView != null ? mFooterView.getIntrinsicHeight() : 0;
final float height =
@@ -2949,7 +2950,7 @@
return view.getHeight();
}
- public int getPositionInLinearLayout(View requestedView) {
+ private int getPositionInLinearLayout(View requestedView) {
ExpandableNotificationRow childInGroup = null;
ExpandableNotificationRow requestedRow = null;
if (isChildInGroup(requestedView)) {
@@ -2958,7 +2959,7 @@
childInGroup = (ExpandableNotificationRow) requestedView;
requestedView = requestedRow = childInGroup.getNotificationParent();
}
- final float scrimTopPadding = mAmbientState.isOnKeyguard() ? 0 : mMinimumPaddings;
+ final float scrimTopPadding = getScrimTopPaddingOrZero();
int position = (int) scrimTopPadding;
int visibleIndex = -1;
ExpandableView lastVisibleChild = null;
@@ -2988,6 +2989,17 @@
return 0;
}
+ /**
+ * Returns the top scrim padding, or zero if the SceneContainer flag is enabled.
+ */
+ private int getScrimTopPaddingOrZero() {
+ if (SceneContainerFlag.isEnabled()) {
+ // the scrim padding is set on the notification placeholder
+ return 0;
+ }
+ return mAmbientState.isOnKeyguard() ? 0 : mMinimumPaddings;
+ }
+
@Override
public void onViewAdded(View child) {
super.onViewAdded(child);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 58b72e5..36930bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -1621,10 +1621,6 @@
return mView.getTransientView(i);
}
- public int getPositionInLinearLayout(ExpandableView row) {
- return mView.getPositionInLinearLayout(row);
- }
-
public NotificationStackScrollLayout getView() {
return mView;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index ee7b5c4..4282fa2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -487,11 +487,8 @@
// expanded. Consider updating these states in updateContentView instead so that we don't
// have to recalculate in every frame.
float currentY = -ambientState.getScrollY();
- if (!ambientState.isOnKeyguard()
- || (ambientState.isBypassEnabled() && ambientState.isPulseExpanding())) {
- // add top padding at the start as long as we're not on the lock screen
- currentY += mNotificationScrimPadding;
- }
+ // add top padding at the start as long as we're not on the lock screen
+ currentY += getScrimTopPaddingOrZero(ambientState);
state.firstViewInShelf = null;
for (int i = 0; i < state.visibleChildren.size(); i++) {
final ExpandableView view = state.visibleChildren.get(i);
@@ -548,11 +545,9 @@
*/
protected void updatePositionsForState(StackScrollAlgorithmState algorithmState,
AmbientState ambientState) {
- if (!ambientState.isOnKeyguard()
- || (ambientState.isBypassEnabled() && ambientState.isPulseExpanding())) {
- algorithmState.mCurrentYPosition += mNotificationScrimPadding;
- algorithmState.mCurrentExpandedYPosition += mNotificationScrimPadding;
- }
+ float scrimTopPadding = getScrimTopPaddingOrZero(ambientState);
+ algorithmState.mCurrentYPosition += scrimTopPadding;
+ algorithmState.mCurrentExpandedYPosition += scrimTopPadding;
int childCount = algorithmState.visibleChildren.size();
for (int i = 0; i < childCount; i++) {
@@ -580,9 +575,7 @@
&& algorithmState.firstViewInShelf != null;
final float shelfHeight = showingShelf ? ambientState.getShelf().getIntrinsicHeight() : 0f;
- final float scrimPadding = ambientState.isOnKeyguard()
- && (!ambientState.isBypassEnabled() || !ambientState.isPulseExpanding())
- ? 0 : mNotificationScrimPadding;
+ final float scrimPadding = getScrimTopPaddingOrZero(ambientState);
final float stackHeight = ambientState.getStackHeight() - shelfHeight - scrimPadding;
final float stackEndHeight = ambientState.getStackEndHeight() - shelfHeight - scrimPadding;
@@ -594,6 +587,21 @@
return stackHeight / stackEndHeight;
}
+ /**
+ * Returns the top scrim padding, or zero if the SceneContainer flag is enabled.
+ */
+ private float getScrimTopPaddingOrZero(AmbientState ambientState) {
+ if (SceneContainerFlag.isEnabled()) {
+ // the scrim padding is set on the notification placeholder
+ return 0f;
+ }
+
+ boolean shouldUsePadding =
+ !ambientState.isOnKeyguard()
+ || (ambientState.isBypassEnabled() && ambientState.isPulseExpanding());
+ return shouldUsePadding ? mNotificationScrimPadding : 0f;
+ }
+
private boolean hasNonClearableNotifs(StackScrollAlgorithmState algorithmState) {
for (int i = 0; i < algorithmState.visibleChildren.size(); i++) {
View child = algorithmState.visibleChildren.get(i);
@@ -664,6 +672,7 @@
float viewEnd = stackTop + viewState.getYTranslation() + viewState.height;
maybeUpdateHeadsUpIsVisible(viewState, ambientState.isShadeExpanded(),
view.mustStayOnScreen(),
+ // TODO(b/332574413) use the position from the HeadsUpNotificationPlaceholder
/* topVisible= */ viewState.getYTranslation() >= mNotificationScrimPadding,
viewEnd, /* hunMax */ ambientState.getMaxHeadsUpTranslation()
);
@@ -891,6 +900,7 @@
// Ensure that the heads up is always visible even when scrolled off.
// NSSL y starts at top of screen in non-split-shade, but below the qs offset
// in split shade, so we only need to inset by the scrim padding in split shade.
+ // TODO(b/332574413) get the clamp inset from HeadsUpNotificationPlaceholder
final float clampInset = ambientState.getUseSplitShade()
? mNotificationScrimPadding : mQuickQsOffsetHeight;
clampHunToTop(clampInset, ambientState.getStackTranslation(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index 1c57346..534d9d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -186,7 +186,6 @@
interactor.configurationBasedDimensions
.map {
when {
- !it.useSplitShade -> 0
it.useLargeScreenHeader -> it.marginTopLargeScreen
else -> it.marginTop
}
@@ -372,7 +371,7 @@
paddingTopDimen,
interactor.topPosition
.sampleCombine(
- keyguardTransitionInteractor.isInTransitionToAnyState,
+ keyguardTransitionInteractor.isInTransition,
shadeInteractor.qsExpansion,
)
.onStart { emit(Triple(0f, false, 0f)) }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 4ce9010..0623bb2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -107,7 +107,6 @@
private int mStatusBarState;
private AnimationStateHandler mAnimationStateHandler;
- private Handler mBgHandler;
private int mHeadsUpInset;
// Used for determining the region for touch interaction
@@ -152,8 +151,7 @@
UiEventLogger uiEventLogger,
JavaAdapter javaAdapter,
ShadeInteractor shadeInteractor,
- AvalancheController avalancheController,
- @Background Handler bgHandler) {
+ AvalancheController avalancheController) {
super(context, logger, handler, globalSettings, systemClock, executor,
accessibilityManagerWrapper, uiEventLogger, avalancheController);
Resources resources = mContext.getResources();
@@ -163,7 +161,6 @@
mGroupMembershipManager = groupMembershipManager;
mVisualStabilityProvider = visualStabilityProvider;
mAvalancheController = avalancheController;
- mBgHandler = bgHandler;
updateResources();
configurationController.addCallback(new ConfigurationController.ConfigurationListener() {
@Override
@@ -405,11 +402,8 @@
// Waiting HUNs in AvalancheController are still promoted to the HUN section and thus
// seen in open shade; clear them so we don't show them again when the shade closes and
// reordering is allowed again.
- int waitingKeysSize = mAvalancheController.getWaitingKeys().size();
- mBgHandler.post(() -> {
- // Do this in the background to avoid missing frames when closing the shade
- mAvalancheController.logDroppedHuns(waitingKeysSize);
- });
+ final int numDropped = mAvalancheController.getWaitingKeys().size();
+ mAvalancheController.logDroppedHunsInBackground(numDropped);
mAvalancheController.clearNext();
// In open shade the first HUN is pinned, and visual stability logic prevents us from
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
index dbe54f3..8aabdf2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
@@ -15,12 +15,14 @@
*/
package com.android.systemui.statusbar.policy
+import android.os.Handler
import android.util.Log
import androidx.annotation.VisibleForTesting
import com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun
import com.android.systemui.statusbar.policy.BaseHeadsUpManager.HeadsUpEntry
@@ -35,7 +37,10 @@
@SysUISingleton
class AvalancheController
@Inject
-constructor(dumpManager: DumpManager, private val uiEventLogger: UiEventLogger) : Dumpable {
+constructor(dumpManager: DumpManager,
+ private val uiEventLogger: UiEventLogger,
+ @Background private val bgHandler: Handler
+) : Dumpable {
private val tag = "AvalancheController"
private val debug = Compile.IS_DEBUG && Log.isLoggable(tag, Log.DEBUG)
@@ -315,7 +320,7 @@
// Remove runnable labels for dropped huns
val listToDrop = nextList.subList(1, nextList.size)
- logDroppedHuns(listToDrop.size)
+ logDroppedHunsInBackground(listToDrop.size)
if (debug) {
// Clear runnable labels
@@ -332,10 +337,13 @@
showNow(headsUpEntryShowing!!, headsUpEntryShowingRunnableList)
}
- fun logDroppedHuns(numDropped: Int) {
- for (n in 1..numDropped) {
- uiEventLogger.log(ThrottleEvent.AVALANCHE_THROTTLING_HUN_DROPPED)
- }
+ fun logDroppedHunsInBackground(numDropped: Int) {
+ bgHandler.post(Runnable {
+ // Do this in the background to avoid missing frames when closing the shade
+ for (n in 1..numDropped) {
+ uiEventLogger.log(ThrottleEvent.AVALANCHE_THROTTLING_HUN_DROPPED)
+ }
+ })
}
fun clearNext() {
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/SettingsSingleThreadBackground.java b/packages/SystemUI/src/com/android/systemui/util/kotlin/SettingsSingleThreadBackground.java
new file mode 100644
index 0000000..e13981d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/SettingsSingleThreadBackground.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.kotlin;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+/**
+ * This is a background executor/dispatcher which guarantees that **within that instance**,
+ * operations will execute in the same order as they were dispatched.
+ * This is useful for background operations like register/unregister where ordering is important.
+ */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface SettingsSingleThreadBackground { }
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/SysUICoroutinesModule.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/SysUICoroutinesModule.kt
index b836016..a03221e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/SysUICoroutinesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/SysUICoroutinesModule.kt
@@ -16,6 +16,7 @@
package com.android.systemui.util.kotlin
+import android.os.Handler
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
@@ -23,15 +24,16 @@
import com.android.systemui.dagger.qualifiers.UiBackground
import dagger.Module
import dagger.Provides
-import java.util.concurrent.Executor
-import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.android.asCoroutineDispatcher
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.newFixedThreadPoolContext
import kotlinx.coroutines.plus
+import java.util.concurrent.Executor
+import kotlin.coroutines.CoroutineContext
private const val LIMIT_BACKGROUND_DISPATCHER_THREADS = true
@@ -78,6 +80,14 @@
}
@Provides
+ @SysUISingleton
+ @SettingsSingleThreadBackground
+ fun settingsBgDispatcher(@Background bgHandler: Handler): CoroutineDispatcher {
+ // Handlers are guaranteed to be sequential so we use that one for now.
+ return bgHandler.asCoroutineDispatcher("SettingsBg")
+ }
+
+ @Provides
@Background
@SysUISingleton
fun bgCoroutineContext(
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java
index 4438763..816f55d 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java
@@ -23,7 +23,7 @@
import android.net.Uri;
import android.provider.Settings;
-import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.util.kotlin.SettingsSingleThreadBackground;
import kotlinx.coroutines.CoroutineDispatcher;
@@ -37,7 +37,7 @@
@Inject
GlobalSettingsImpl(ContentResolver contentResolver,
- @Background CoroutineDispatcher bgDispatcher) {
+ @SettingsSingleThreadBackground CoroutineDispatcher bgDispatcher) {
mContentResolver = contentResolver;
mBgDispatcher = bgDispatcher;
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java
index 38ad5d0..9c98f43 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java
@@ -22,13 +22,13 @@
import androidx.annotation.NonNull;
-import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.settings.UserTracker;
-
-import kotlinx.coroutines.CoroutineDispatcher;
+import com.android.systemui.util.kotlin.SettingsSingleThreadBackground;
import javax.inject.Inject;
+import kotlinx.coroutines.CoroutineDispatcher;
+
class SecureSettingsImpl implements SecureSettings {
private final ContentResolver mContentResolver;
private final UserTracker mUserTracker;
@@ -36,7 +36,7 @@
@Inject
SecureSettingsImpl(ContentResolver contentResolver, UserTracker userTracker,
- @Background CoroutineDispatcher bgDispatcher) {
+ @SettingsSingleThreadBackground CoroutineDispatcher bgDispatcher) {
mContentResolver = contentResolver;
mUserTracker = userTracker;
mBgDispatcher = bgDispatcher;
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java
index 68cc753..406d95b 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java
@@ -22,13 +22,13 @@
import androidx.annotation.NonNull;
-import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.settings.UserTracker;
-
-import kotlinx.coroutines.CoroutineDispatcher;
+import com.android.systemui.util.kotlin.SettingsSingleThreadBackground;
import javax.inject.Inject;
+import kotlinx.coroutines.CoroutineDispatcher;
+
class SystemSettingsImpl implements SystemSettings {
private final ContentResolver mContentResolver;
private final UserTracker mUserTracker;
@@ -36,7 +36,7 @@
@Inject
SystemSettingsImpl(ContentResolver contentResolver, UserTracker userTracker,
- @Background CoroutineDispatcher bgDispatcher) {
+ @SettingsSingleThreadBackground CoroutineDispatcher bgDispatcher) {
mContentResolver = contentResolver;
mUserTracker = userTracker;
mBgCoroutineDispatcher = bgDispatcher;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 9a99ed7..1e3ee28 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -27,6 +27,7 @@
import android.hardware.biometrics.PromptInfo
import android.hardware.biometrics.PromptVerticalListContentView
import android.hardware.face.FaceSensorPropertiesInternal
+import android.hardware.fingerprint.FingerprintManager
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.os.Handler
import android.os.IBinder
@@ -88,9 +89,8 @@
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-import org.mockito.junit.MockitoJUnit
import org.mockito.Mockito.`when` as whenever
-
+import org.mockito.junit.MockitoJUnit
private const val OP_PACKAGE_NAME = "biometric.testapp"
@@ -99,33 +99,21 @@
@SmallTest
open class AuthContainerViewTest : SysuiTestCase() {
- @JvmField @Rule
- var mockitoRule = MockitoJUnit.rule()
+ @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
- @Mock
- lateinit var callback: AuthDialogCallback
- @Mock
- lateinit var userManager: UserManager
- @Mock
- lateinit var lockPatternUtils: LockPatternUtils
- @Mock
- lateinit var wakefulnessLifecycle: WakefulnessLifecycle
- @Mock
- lateinit var panelInteractionDetector: AuthDialogPanelInteractionDetector
- @Mock
- lateinit var windowToken: IBinder
- @Mock
- lateinit var interactionJankMonitor: InteractionJankMonitor
- @Mock
- lateinit var vibrator: VibratorHelper
- @Mock
- lateinit var udfpsUtils: UdfpsUtils
- @Mock
- lateinit var authController: AuthController
- @Mock
- lateinit var selectedUserInteractor: SelectedUserInteractor
- @Mock
- private lateinit var packageManager: PackageManager
+ @Mock lateinit var callback: AuthDialogCallback
+ @Mock lateinit var userManager: UserManager
+ @Mock lateinit var fingerprintManager: FingerprintManager
+ @Mock lateinit var lockPatternUtils: LockPatternUtils
+ @Mock lateinit var wakefulnessLifecycle: WakefulnessLifecycle
+ @Mock lateinit var panelInteractionDetector: AuthDialogPanelInteractionDetector
+ @Mock lateinit var windowToken: IBinder
+ @Mock lateinit var interactionJankMonitor: InteractionJankMonitor
+ @Mock lateinit var vibrator: VibratorHelper
+ @Mock lateinit var udfpsUtils: UdfpsUtils
+ @Mock lateinit var authController: AuthController
+ @Mock lateinit var selectedUserInteractor: SelectedUserInteractor
+ @Mock private lateinit var packageManager: PackageManager
@Mock private lateinit var activityTaskManager: ActivityTaskManager
private lateinit var displayRepository: FakeDisplayRepository
@@ -141,11 +129,12 @@
private val fingerprintRepository = FakeFingerprintPropertyRepository()
private val displayStateRepository = FakeDisplayStateRepository()
private val credentialInteractor = FakeCredentialInteractor()
- private val bpCredentialInteractor = PromptCredentialInteractor(
- Dispatchers.Main.immediate,
- biometricPromptRepository,
- credentialInteractor,
- )
+ private val bpCredentialInteractor =
+ PromptCredentialInteractor(
+ Dispatchers.Main.immediate,
+ biometricPromptRepository,
+ credentialInteractor,
+ )
private val promptSelectorInteractor by lazy {
PromptSelectorInteractorImpl(
fingerprintRepository,
@@ -166,22 +155,26 @@
displayStateInteractor =
DisplayStateInteractorImpl(
- testScope.backgroundScope,
- mContext,
- fakeExecutor,
- displayStateRepository,
- displayRepository,
+ testScope.backgroundScope,
+ mContext,
+ fakeExecutor,
+ displayStateRepository,
+ displayRepository,
)
udfpsOverlayInteractor =
- UdfpsOverlayInteractor(
- context,
- authController,
- selectedUserInteractor,
- testScope.backgroundScope,
- )
+ UdfpsOverlayInteractor(
+ context,
+ authController,
+ selectedUserInteractor,
+ fingerprintManager,
+ testScope.backgroundScope,
+ )
biometricStatusInteractor =
- BiometricStatusInteractorImpl(activityTaskManager, biometricStatusRepository,
- fingerprintRepository)
+ BiometricStatusInteractorImpl(
+ activityTaskManager,
+ biometricStatusRepository,
+ fingerprintRepository
+ )
iconProvider = IconProvider(context)
// Set up default logo icon
whenever(packageManager.getApplicationIcon(OP_PACKAGE_NAME)).thenReturn(defaultLogoIcon)
@@ -198,10 +191,8 @@
@Test
fun testNotifiesAnimatedIn() {
initializeFingerprintContainer()
- verify(callback).onDialogAnimatedIn(
- authContainer?.requestId ?: 0L,
- true /* startFingerprintNow */
- )
+ verify(callback)
+ .onDialogAnimatedIn(authContainer?.requestId ?: 0L, true /* startFingerprintNow */)
}
@Test
@@ -246,10 +237,8 @@
waitForIdleSync()
// attaching the view resets the state and allows this to happen again
- verify(callback).onDialogAnimatedIn(
- authContainer?.requestId ?: 0L,
- true /* startFingerprintNow */
- )
+ verify(callback)
+ .onDialogAnimatedIn(authContainer?.requestId ?: 0L, true /* startFingerprintNow */)
}
@Test
@@ -274,10 +263,8 @@
// the first time is triggered by initializeFingerprintContainer()
// the second time was triggered by dismissWithoutCallback()
- verify(callback, times(2)).onDialogAnimatedIn(
- authContainer?.requestId ?: 0L,
- true /* startFingerprintNow */
- )
+ verify(callback, times(2))
+ .onDialogAnimatedIn(authContainer?.requestId ?: 0L, true /* startFingerprintNow */)
}
@Test
@@ -288,18 +275,18 @@
verify(panelInteractionDetector).disable()
}
-
@Test
fun testActionAuthenticated_sendsDismissedAuthenticated() {
val container = initializeFingerprintContainer()
container.mBiometricCallback.onAuthenticated()
waitForIdleSync()
- verify(callback).onDismissed(
+ verify(callback)
+ .onDismissed(
eq(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED),
eq<ByteArray?>(null), /* credentialAttestation */
eq(authContainer?.requestId ?: 0L)
- )
+ )
assertThat(container.parent).isNull()
}
@@ -309,15 +296,17 @@
container.mBiometricCallback.onUserCanceled()
waitForIdleSync()
- verify(callback).onSystemEvent(
+ verify(callback)
+ .onSystemEvent(
eq(BiometricConstants.BIOMETRIC_SYSTEM_EVENT_EARLY_USER_CANCEL),
eq(authContainer?.requestId ?: 0L)
- )
- verify(callback).onDismissed(
+ )
+ verify(callback)
+ .onDismissed(
eq(AuthDialogCallback.DISMISSED_USER_CANCELED),
eq<ByteArray?>(null), /* credentialAttestation */
eq(authContainer?.requestId ?: 0L)
- )
+ )
assertThat(container.parent).isNull()
}
@@ -327,19 +316,21 @@
container.mBiometricCallback.onButtonNegative()
waitForIdleSync()
- verify(callback).onDismissed(
+ verify(callback)
+ .onDismissed(
eq(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE),
eq<ByteArray?>(null), /* credentialAttestation */
eq(authContainer?.requestId ?: 0L)
- )
+ )
assertThat(container.parent).isNull()
}
@Test
fun testActionTryAgain_sendsTryAgain() {
- val container = initializeFingerprintContainer(
- authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK
- )
+ val container =
+ initializeFingerprintContainer(
+ authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK
+ )
container.mBiometricCallback.onButtonTryAgain()
waitForIdleSync()
@@ -352,21 +343,24 @@
container.mBiometricCallback.onError()
waitForIdleSync()
- verify(callback).onDismissed(
+ verify(callback)
+ .onDismissed(
eq(AuthDialogCallback.DISMISSED_ERROR),
eq<ByteArray?>(null), /* credentialAttestation */
eq(authContainer?.requestId ?: 0L)
- )
+ )
assertThat(authContainer!!.parent).isNull()
}
@Ignore("b/279650412")
@Test
fun testActionUseDeviceCredential_sendsOnDeviceCredentialPressed() {
- val container = initializeFingerprintContainer(
- authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK or
- BiometricManager.Authenticators.DEVICE_CREDENTIAL
- )
+ val container =
+ initializeFingerprintContainer(
+ authenticators =
+ BiometricManager.Authenticators.BIOMETRIC_WEAK or
+ BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ )
container.mBiometricCallback.onUseDeviceCredential()
waitForIdleSync()
@@ -376,10 +370,12 @@
@Test
fun testAnimateToCredentialUI_invokesStartTransitionToCredentialUI() {
- val container = initializeFingerprintContainer(
- authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK or
- BiometricManager.Authenticators.DEVICE_CREDENTIAL
- )
+ val container =
+ initializeFingerprintContainer(
+ authenticators =
+ BiometricManager.Authenticators.BIOMETRIC_WEAK or
+ BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ )
container.animateToCredentialUI(false)
waitForIdleSync()
@@ -395,10 +391,12 @@
@Test
fun testAnimateToCredentialUI_rotateCredentialUI() {
- val container = initializeFingerprintContainer(
- authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK or
- BiometricManager.Authenticators.DEVICE_CREDENTIAL
- )
+ val container =
+ initializeFingerprintContainer(
+ authenticators =
+ BiometricManager.Authenticators.BIOMETRIC_WEAK or
+ BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ )
container.animateToCredentialUI(false)
waitForIdleSync()
@@ -437,13 +435,12 @@
mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
var isButtonClicked = false
val contentView =
- PromptContentViewWithMoreOptionsButton.Builder()
- .setMoreOptionsButtonListener(
- fakeExecutor) { _, _ -> isButtonClicked = true }
- .build()
+ PromptContentViewWithMoreOptionsButton.Builder()
+ .setMoreOptionsButtonListener(fakeExecutor) { _, _ -> isButtonClicked = true }
+ .build()
val container =
- initializeFingerprintContainer(contentViewWithMoreOptionsButton = contentView)
+ initializeFingerprintContainer(contentViewWithMoreOptionsButton = contentView)
waitForIdleSync()
@@ -461,9 +458,9 @@
@Test
fun testShowCredentialUI_withDescription() {
val container =
- initializeFingerprintContainer(
- authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
- )
+ initializeFingerprintContainer(
+ authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ )
waitForIdleSync()
assertThat(container.hasCredentialView()).isTrue()
@@ -475,10 +472,10 @@
mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
val container =
- initializeFingerprintContainer(
- authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL,
- verticalListContentView = PromptVerticalListContentView.Builder().build()
- )
+ initializeFingerprintContainer(
+ authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL,
+ verticalListContentView = PromptVerticalListContentView.Builder().build()
+ )
// Two-step credential view should show -
// 1. biometric prompt without sensor 2. credential view ui
waitForIdleSync()
@@ -497,14 +494,14 @@
mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
val contentView =
- PromptContentViewWithMoreOptionsButton.Builder()
- .setMoreOptionsButtonListener(fakeExecutor) { _, _ -> }
- .build()
+ PromptContentViewWithMoreOptionsButton.Builder()
+ .setMoreOptionsButtonListener(fakeExecutor) { _, _ -> }
+ .build()
val container =
- initializeFingerprintContainer(
- authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL,
- contentViewWithMoreOptionsButton = contentView
- )
+ initializeFingerprintContainer(
+ authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL,
+ contentViewWithMoreOptionsButton = contentView
+ )
waitForIdleSync()
assertThat(container.hasCredentialView()).isTrue()
@@ -514,13 +511,13 @@
@Test
fun testCredentialViewUsesEffectiveUserId() {
whenever(userManager.getCredentialOwnerProfile(anyInt())).thenReturn(200)
- whenever(lockPatternUtils.getKeyguardStoredPasswordQuality(eq(200))).thenReturn(
- DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
- )
+ whenever(lockPatternUtils.getKeyguardStoredPasswordQuality(eq(200)))
+ .thenReturn(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING)
- val container = initializeFingerprintContainer(
- authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
- )
+ val container =
+ initializeFingerprintContainer(
+ authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ )
waitForIdleSync()
assertThat(container.hasCredentialPatternView()).isTrue()
@@ -531,9 +528,8 @@
fun testCredentialUI_disablesClickingOnBackground() {
val container = initializeCredentialPasswordContainer()
assertThat(container.hasBiometricPrompt()).isFalse()
- assertThat(
- container.findViewById<View>(R.id.background)?.isImportantForAccessibility
- ).isFalse()
+ assertThat(container.findViewById<View>(R.id.background)?.isImportantForAccessibility)
+ .isFalse()
container.findViewById<View>(R.id.background)?.performClick()
waitForIdleSync()
@@ -552,7 +548,7 @@
fun testLayoutParams_hasShowWhenLockedFlag() {
val layoutParams = AuthContainerView.getLayoutParams(windowToken, "")
assertThat((layoutParams.flags and WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED) != 0)
- .isTrue()
+ .isTrue()
}
@Test
@@ -590,20 +586,20 @@
}
private fun initializeCredentialPasswordContainer(
- addToView: Boolean = true,
+ addToView: Boolean = true,
): TestAuthContainerView {
whenever(userManager.getCredentialOwnerProfile(anyInt())).thenReturn(20)
- whenever(lockPatternUtils.getKeyguardStoredPasswordQuality(eq(20))).thenReturn(
- DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
- )
+ whenever(lockPatternUtils.getKeyguardStoredPasswordQuality(eq(20)))
+ .thenReturn(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC)
// In the credential view, clicking on the background (to cancel authentication) is not
// valid. Thus, the listener should be null, and it should not be in the accessibility
// hierarchy.
- val container = initializeFingerprintContainer(
+ val container =
+ initializeFingerprintContainer(
authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL,
addToView = addToView,
- )
+ )
waitForIdleSync()
assertThat(container.hasCredentialPasswordView()).isTrue()
@@ -615,26 +611,28 @@
addToView: Boolean = true,
verticalListContentView: PromptVerticalListContentView? = null,
contentViewWithMoreOptionsButton: PromptContentViewWithMoreOptionsButton? = null,
- ) = initializeContainer(
- TestAuthContainerView(
- authenticators = authenticators,
- fingerprintProps = fingerprintSensorPropertiesInternal(),
+ ) =
+ initializeContainer(
+ TestAuthContainerView(
+ authenticators = authenticators,
+ fingerprintProps = fingerprintSensorPropertiesInternal(),
verticalListContentView = verticalListContentView,
- ),
- addToView
- )
+ ),
+ addToView
+ )
private fun initializeCoexContainer(
authenticators: Int = BiometricManager.Authenticators.BIOMETRIC_WEAK,
addToView: Boolean = true
- ) = initializeContainer(
- TestAuthContainerView(
- authenticators = authenticators,
- fingerprintProps = fingerprintSensorPropertiesInternal(),
- faceProps = faceSensorPropertiesInternal()
- ),
- addToView
- )
+ ) =
+ initializeContainer(
+ TestAuthContainerView(
+ authenticators = authenticators,
+ fingerprintProps = fingerprintSensorPropertiesInternal(),
+ faceProps = faceSensorPropertiesInternal()
+ ),
+ addToView
+ )
private fun initializeContainer(
view: TestAuthContainerView,
@@ -655,47 +653,50 @@
faceProps: List<FaceSensorPropertiesInternal> = listOf(),
verticalListContentView: PromptVerticalListContentView? = null,
contentViewWithMoreOptionsButton: PromptContentViewWithMoreOptionsButton? = null,
- ) : AuthContainerView(
- Config().apply {
- mContext = this@AuthContainerViewTest.context
- mCallback = callback
- mSensorIds = (fingerprintProps.map { it.sensorId } +
- faceProps.map { it.sensorId }).toIntArray()
- mSkipAnimation = true
- mPromptInfo = PromptInfo().apply {
- this.authenticators = authenticators
- if (verticalListContentView != null) {
- this.contentView = verticalListContentView
- } else if (contentViewWithMoreOptionsButton != null) {
- this.contentView = contentViewWithMoreOptionsButton
- }
- }
- mOpPackageName = OP_PACKAGE_NAME
- },
- testScope.backgroundScope,
- fingerprintProps,
- faceProps,
- wakefulnessLifecycle,
- panelInteractionDetector,
- userManager,
- lockPatternUtils,
- interactionJankMonitor,
- { promptSelectorInteractor },
- PromptViewModel(
- displayStateInteractor,
- promptSelectorInteractor,
- context,
- udfpsOverlayInteractor,
- biometricStatusInteractor,
- udfpsUtils,
- iconProvider,
- activityTaskManager
- ),
- { credentialViewModel },
- Handler(TestableLooper.get(this).looper),
- fakeExecutor,
- vibrator
- ) {
+ ) :
+ AuthContainerView(
+ Config().apply {
+ mContext = this@AuthContainerViewTest.context
+ mCallback = callback
+ mSensorIds =
+ (fingerprintProps.map { it.sensorId } + faceProps.map { it.sensorId })
+ .toIntArray()
+ mSkipAnimation = true
+ mPromptInfo =
+ PromptInfo().apply {
+ this.authenticators = authenticators
+ if (verticalListContentView != null) {
+ this.contentView = verticalListContentView
+ } else if (contentViewWithMoreOptionsButton != null) {
+ this.contentView = contentViewWithMoreOptionsButton
+ }
+ }
+ mOpPackageName = OP_PACKAGE_NAME
+ },
+ testScope.backgroundScope,
+ fingerprintProps,
+ faceProps,
+ wakefulnessLifecycle,
+ panelInteractionDetector,
+ userManager,
+ lockPatternUtils,
+ interactionJankMonitor,
+ { promptSelectorInteractor },
+ PromptViewModel(
+ displayStateInteractor,
+ promptSelectorInteractor,
+ context,
+ udfpsOverlayInteractor,
+ biometricStatusInteractor,
+ udfpsUtils,
+ iconProvider,
+ activityTaskManager
+ ),
+ { credentialViewModel },
+ Handler(TestableLooper.get(this).looper),
+ fakeExecutor,
+ vibrator
+ ) {
override fun postOnAnimation(runnable: Runnable) {
runnable.run()
}
@@ -717,8 +718,10 @@
val layoutParams = AuthContainerView.getLayoutParams(windowToken, "")
val lpFlags = layoutParams.flags
- assertThat((lpFlags and WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS)
- != 0).isTrue()
+ assertThat(
+ (lpFlags and WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) != 0
+ )
+ .isTrue()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
index 3d63c5b..13f2c72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.biometrics.domain.interactor
import android.graphics.Rect
+import android.hardware.fingerprint.FingerprintManager
import android.view.MotionEvent
import android.view.Surface
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -51,6 +52,7 @@
private lateinit var testScope: TestScope
+ @Mock private lateinit var fingerprintManager: FingerprintManager
@Mock private lateinit var authController: AuthController
@Captor private lateinit var authControllerCallback: ArgumentCaptor<AuthController.Callback>
@@ -111,6 +113,7 @@
context,
authController,
selectedUserInteractor,
+ fingerprintManager,
testScope.backgroundScope
)
testScope.runCurrent()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepositoryTest.kt
deleted file mode 100644
index b986c52..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepositoryTest.kt
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyboard.shortcut.data.repository
-
-import android.view.KeyEvent
-import android.view.KeyboardShortcutGroup
-import android.view.KeyboardShortcutInfo
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.keyboard.shortcut.shared.model.Shortcut
-import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
-import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand
-import com.android.systemui.keyboard.shortcut.shared.model.shortcutCategory
-import com.android.systemui.keyboard.shortcut.shortcutHelperCategoriesRepository
-import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper
-import com.android.systemui.kosmos.testDispatcher
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.testKosmos
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.runTest
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class ShortcutHelperCategoriesRepositoryTest : SysuiTestCase() {
- @OptIn(ExperimentalCoroutinesApi::class)
- private val kosmos = testKosmos().also { it.testDispatcher = UnconfinedTestDispatcher() }
- private val repo = kosmos.shortcutHelperCategoriesRepository
- private val helper = kosmos.shortcutHelperTestHelper
- private val testScope = kosmos.testScope
-
- @Test
- fun stateActive_imeShortcuts_shortcutInfoCorrectlyConverted() =
- testScope.runTest {
- helper.setImeShortcuts(imeShortcutsGroupWithPreviousLanguageSwitchShortcut)
- val imeShortcutCategory by collectLastValue(repo.imeShortcutsCategory)
-
- helper.showFromActivity()
-
- assertThat(imeShortcutCategory)
- .isEqualTo(expectedImeShortcutCategoryWithPreviousLanguageSwitchShortcut)
- }
-
- @Test
- fun stateActive_imeShortcuts_onlyUnsupportedShortcuts_discardsAll() =
- testScope.runTest {
- helper.setImeShortcuts(imeShortcutsGroupWithUnsupportedShortcutModifiers)
- val imeShortcutCategory by collectLastValue(repo.imeShortcutsCategory)
-
- helper.showFromActivity()
-
- assertThat(imeShortcutCategory).isEqualTo(null)
- }
-
- private val switchToPreviousLanguageCommand =
- ShortcutCommand(
- listOf(KeyEvent.META_CTRL_ON, KeyEvent.META_SHIFT_ON, KeyEvent.KEYCODE_SPACE)
- )
-
- private val expectedImeShortcutCategoryWithDiscardedUnsupportedShortcuts =
- shortcutCategory(ShortcutCategoryType.IME) { subCategory("input", emptyList()) }
-
- private val switchToPreviousLanguageKeyboardShortcutInfo =
- KeyboardShortcutInfo(
- /* label = */ "switch to previous language",
- /* keycode = */ switchToPreviousLanguageCommand.keyCodes[2],
- /* modifiers = */ switchToPreviousLanguageCommand.keyCodes[0] or
- switchToPreviousLanguageCommand.keyCodes[1],
- )
-
- private val expectedImeShortcutCategoryWithPreviousLanguageSwitchShortcut =
- shortcutCategory(ShortcutCategoryType.IME) {
- subCategory(
- "input",
- listOf(
- Shortcut(
- switchToPreviousLanguageKeyboardShortcutInfo.label!!.toString(),
- listOf(switchToPreviousLanguageCommand)
- )
- )
- )
- }
-
- private val imeShortcutsGroupWithPreviousLanguageSwitchShortcut =
- listOf(
- KeyboardShortcutGroup(
- "input",
- listOf(
- switchToPreviousLanguageKeyboardShortcutInfo,
- )
- )
- )
-
- private val shortcutInfoWithUnsupportedModifier =
- KeyboardShortcutInfo(
- /* label = */ "unsupported shortcut",
- /* keycode = */ KeyEvent.KEYCODE_SPACE,
- /* modifiers = */ 32
- )
-
- private val imeShortcutsGroupWithUnsupportedShortcutModifiers =
- listOf(
- KeyboardShortcutGroup(
- "input",
- listOf(
- shortcutInfoWithUnsupportedModifier,
- )
- )
- )
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepositoryTest.kt
index 3caa8f6..0b4e6a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepositoryTest.kt
@@ -65,17 +65,6 @@
}
@Test
- fun state_activeThroughActivity_virtualKeyboardActive_emitsActiveWithVirtualDeviceId() =
- testScope.runTest {
- val state by collectLastValue(repo.state)
-
- fakeInputManager.addVirtualKeyboard()
- helper.showFromActivity()
-
- assertThat(state).isEqualTo(ShortcutHelperState.Active(VIRTUAL_KEYBOARD))
- }
-
- @Test
fun state_activeThroughActivity_physicalKeyboardActive_emitsActiveWithDeviceId() =
testScope.runTest {
val deviceId = 456
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
new file mode 100644
index 0000000..c2814bd
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.data.source
+
+import android.view.KeyEvent
+import android.view.KeyboardShortcutGroup
+import android.view.KeyboardShortcutInfo
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory
+import com.android.systemui.keyboard.shortcut.shared.model.shortcut
+import com.android.systemui.res.R
+
+object TestShortcuts {
+
+ private val shortcutInfoWithRepeatedLabel =
+ KeyboardShortcutInfo(
+ /* label = */ "Shortcut with repeated label",
+ /* keycode = */ KeyEvent.KEYCODE_H,
+ /* modifiers = */ KeyEvent.META_META_ON,
+ )
+
+ private val shortcutInfoWithRepeatedLabelAlternate =
+ KeyboardShortcutInfo(
+ /* label = */ shortcutInfoWithRepeatedLabel.label,
+ /* keycode = */ KeyEvent.KEYCODE_L,
+ /* modifiers = */ KeyEvent.META_META_ON,
+ )
+
+ private val shortcutInfoWithRepeatedLabelSecondAlternate =
+ KeyboardShortcutInfo(
+ /* label = */ shortcutInfoWithRepeatedLabel.label,
+ /* keycode = */ KeyEvent.KEYCODE_M,
+ /* modifiers = */ KeyEvent.META_SHIFT_ON,
+ )
+
+ private val shortcutWithGroupedRepeatedLabel =
+ shortcut(shortcutInfoWithRepeatedLabel.label!!.toString()) {
+ command {
+ key(R.drawable.ic_ksh_key_meta)
+ key("H")
+ }
+ command {
+ key(R.drawable.ic_ksh_key_meta)
+ key("L")
+ }
+ command {
+ key("Shift")
+ key("M")
+ }
+ }
+
+ private val standardShortcutInfo1 =
+ KeyboardShortcutInfo(
+ /* label = */ "Standard shortcut 1",
+ /* keycode = */ KeyEvent.KEYCODE_N,
+ /* modifiers = */ KeyEvent.META_META_ON,
+ )
+
+ private val standardShortcut1 =
+ shortcut(standardShortcutInfo1.label!!.toString()) {
+ command {
+ key(R.drawable.ic_ksh_key_meta)
+ key("N")
+ }
+ }
+
+ private val standardShortcutInfo2 =
+ KeyboardShortcutInfo(
+ /* label = */ "Standard shortcut 2",
+ /* keycode = */ KeyEvent.KEYCODE_Z,
+ /* modifiers = */ KeyEvent.META_ALT_ON or KeyEvent.META_SHIFT_ON,
+ )
+
+ private val standardShortcut2 =
+ shortcut(standardShortcutInfo2.label!!.toString()) {
+ command {
+ key("Alt")
+ key("Shift")
+ key("Z")
+ }
+ }
+
+ private val standardShortcutInfo3 =
+ KeyboardShortcutInfo(
+ /* label = */ "Standard shortcut 3",
+ /* keycode = */ KeyEvent.KEYCODE_J,
+ /* modifiers = */ KeyEvent.META_CTRL_ON,
+ )
+
+ private val standardShortcut3 =
+ shortcut(standardShortcutInfo3.label!!.toString()) {
+ command {
+ key("Ctrl")
+ key("J")
+ }
+ }
+
+ private val shortcutInfoWithUnsupportedModifiers =
+ KeyboardShortcutInfo(
+ /* label = */ "Shortcut with unsupported modifiers",
+ /* keycode = */ KeyEvent.KEYCODE_A,
+ /* modifiers = */ KeyEvent.META_META_ON or KeyEvent.KEYCODE_SPACE,
+ )
+
+ private val groupWithRepeatedShortcutLabels =
+ KeyboardShortcutGroup(
+ "Group with duplicate labels",
+ listOf(
+ shortcutInfoWithRepeatedLabel,
+ shortcutInfoWithRepeatedLabelAlternate,
+ shortcutInfoWithRepeatedLabelSecondAlternate
+ )
+ )
+
+ private val subCategoryWithGroupedRepeatedShortcutLabels =
+ ShortcutSubCategory(
+ label = groupWithRepeatedShortcutLabels.label!!.toString(),
+ shortcuts = listOf(shortcutWithGroupedRepeatedLabel)
+ )
+
+ private val groupWithStandardShortcutInfo =
+ KeyboardShortcutGroup("Standard group", listOf(standardShortcutInfo1))
+
+ private val subCategoryWithStandardShortcut =
+ ShortcutSubCategory(
+ label = groupWithStandardShortcutInfo.label!!.toString(),
+ shortcuts = listOf(standardShortcut1)
+ )
+
+ private val groupWithOnlyUnsupportedModifierShortcut =
+ KeyboardShortcutGroup(
+ "Group with unsupported modifiers",
+ listOf(shortcutInfoWithUnsupportedModifiers)
+ )
+
+ private val groupWithSupportedAndUnsupportedModifierShortcut =
+ KeyboardShortcutGroup(
+ "Group with mix of supported and not supported modifiers",
+ listOf(standardShortcutInfo3, shortcutInfoWithUnsupportedModifiers)
+ )
+
+ private val subCategoryWithUnsupportedShortcutsRemoved =
+ ShortcutSubCategory(
+ groupWithSupportedAndUnsupportedModifierShortcut.label!!.toString(),
+ listOf(standardShortcut3)
+ )
+
+ private val standardGroup1 =
+ KeyboardShortcutGroup(
+ "Standard group 1",
+ listOf(standardShortcutInfo1, standardShortcutInfo2, standardShortcutInfo3)
+ )
+
+ private val standardSubCategory1 =
+ ShortcutSubCategory(
+ standardGroup1.label!!.toString(),
+ listOf(standardShortcut1, standardShortcut2, standardShortcut3)
+ )
+
+ private val standardGroup2 =
+ KeyboardShortcutGroup(
+ "Standard group 2",
+ listOf(standardShortcutInfo3, standardShortcutInfo2, standardShortcutInfo1)
+ )
+
+ private val standardSubCategory2 =
+ ShortcutSubCategory(
+ standardGroup2.label!!.toString(),
+ listOf(standardShortcut3, standardShortcut2, standardShortcut1)
+ )
+ private val standardGroup3 =
+ KeyboardShortcutGroup(
+ "Standard group 3",
+ listOf(standardShortcutInfo2, standardShortcutInfo1)
+ )
+
+ private val standardSubCategory3 =
+ ShortcutSubCategory(
+ standardGroup3.label!!.toString(),
+ listOf(standardShortcut2, standardShortcut1)
+ )
+ val imeGroups = listOf(standardGroup1, standardGroup2, standardGroup3)
+ val imeCategory =
+ ShortcutCategory(
+ type = ShortcutCategoryType.IME,
+ subCategories = listOf(standardSubCategory1, standardSubCategory2, standardSubCategory3)
+ )
+
+ val systemGroups = listOf(standardGroup3, standardGroup2, standardGroup1)
+ val systemCategory =
+ ShortcutCategory(
+ type = ShortcutCategoryType.SYSTEM,
+ subCategories = listOf(standardSubCategory3, standardSubCategory2, standardSubCategory1)
+ )
+
+ val multitaskingGroups = listOf(standardGroup2, standardGroup1)
+ val multitaskingCategory =
+ ShortcutCategory(
+ type = ShortcutCategoryType.MULTI_TASKING,
+ subCategories = listOf(standardSubCategory2, standardSubCategory1)
+ )
+
+ val groupsWithDuplicateShortcutLabels =
+ listOf(groupWithRepeatedShortcutLabels, groupWithStandardShortcutInfo)
+
+ val subCategoriesWithGroupedDuplicatedShortcutLabels =
+ listOf(subCategoryWithGroupedRepeatedShortcutLabels, subCategoryWithStandardShortcut)
+
+ val groupsWithUnsupportedModifier =
+ listOf(
+ groupWithStandardShortcutInfo,
+ groupWithOnlyUnsupportedModifierShortcut,
+ groupWithSupportedAndUnsupportedModifierShortcut
+ )
+
+ val subCategoriesWithUnsupportedModifiersRemoved =
+ listOf(subCategoryWithStandardShortcut, subCategoryWithUnsupportedShortcutsRemoved)
+
+ val groupsWithOnlyUnsupportedModifiers = listOf(groupWithOnlyUnsupportedModifierShortcut)
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt
index c00e7e79..c6a7565 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt
@@ -16,18 +16,16 @@
package com.android.systemui.keyboard.shortcut.domain.interactor
-import android.view.KeyEvent
-import android.view.KeyboardShortcutGroup
-import android.view.KeyboardShortcutInfo
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyboard.shortcut.data.source.FakeKeyboardShortcutGroupsSource
+import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
-import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType
-import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory
-import com.android.systemui.keyboard.shortcut.shared.model.shortcut
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.IME
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MULTI_TASKING
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.SYSTEM
import com.android.systemui.keyboard.shortcut.shortcutHelperCategoriesInteractor
import com.android.systemui.keyboard.shortcut.shortcutHelperMultiTaskingShortcutsSource
import com.android.systemui.keyboard.shortcut.shortcutHelperSystemShortcutsSource
@@ -56,15 +54,16 @@
it.shortcutHelperSystemShortcutsSource = systemShortcutsSource
it.shortcutHelperMultiTaskingShortcutsSource = multitaskingShortcutsSource
}
+
private val testScope = kosmos.testScope
private val interactor = kosmos.shortcutHelperCategoriesInteractor
private val helper = kosmos.shortcutHelperTestHelper
@Before
- fun setUp() {
- // Setting these sources as empty temporarily. Will be populated in follow up CL.
- systemShortcutsSource.setGroups(emptyList())
- multitaskingShortcutsSource.setGroups(emptyList())
+ fun setShortcuts() {
+ helper.setImeShortcuts(TestShortcuts.imeGroups)
+ systemShortcutsSource.setGroups(TestShortcuts.systemGroups)
+ multitaskingShortcutsSource.setGroups(TestShortcuts.multitaskingGroups)
}
@Test
@@ -78,12 +77,17 @@
@Test
fun categories_stateActive_emitsAllCategoriesInOrder() =
testScope.runTest {
- helper.setImeShortcuts(imeShortcutGroups)
val categories by collectLastValue(interactor.shortcutCategories)
helper.showFromActivity()
- assertThat(categories).containsExactly(imeShortcutCategory).inOrder()
+ assertThat(categories)
+ .containsExactly(
+ TestShortcuts.systemCategory,
+ TestShortcuts.multitaskingCategory,
+ TestShortcuts.imeCategory,
+ )
+ .inOrder()
}
@Test
@@ -96,159 +100,177 @@
assertThat(categories).isEmpty()
}
- fun categories_stateActive_emitsGroupedShortcuts() =
+ @Test
+ fun categories_stateActive_imeShortcutsWithDuplicateLabels_emitsGroupedShortcuts() =
testScope.runTest {
- helper.setImeShortcuts(imeShortcutsGroupsWithDuplicateLabels)
+ helper.setImeShortcuts(TestShortcuts.groupsWithDuplicateShortcutLabels)
+
val categories by collectLastValue(interactor.shortcutCategories)
helper.showFromActivity()
- assertThat(categories).containsExactly(expectedGroupedShortcutCategories)
- }
-
- private val switchToNextLanguageShortcut =
- shortcut(label = "switch to next language") {
- command(KeyEvent.META_CTRL_ON, KeyEvent.KEYCODE_SPACE)
- }
-
- private val switchToNextLanguageKeyboardShortcutInfo =
- KeyboardShortcutInfo(
- /* label = */ switchToNextLanguageShortcut.label,
- /* keycode = */ switchToNextLanguageShortcut.commands[0].keyCodes[1],
- /* modifiers = */ switchToNextLanguageShortcut.commands[0].keyCodes[0],
- )
-
- private val switchToNextLanguageShortcutAlternative =
- shortcut("switch to next language") {
- command(KeyEvent.META_CTRL_ON, KeyEvent.KEYCODE_SPACE)
- }
-
- private val switchToNextLanguageKeyboardShortcutInfoAlternative =
- KeyboardShortcutInfo(
- /* label = */ switchToNextLanguageShortcutAlternative.label,
- /* keycode = */ switchToNextLanguageShortcutAlternative.commands[0].keyCodes[1],
- /* modifiers = */ switchToNextLanguageShortcutAlternative.commands[0].keyCodes[0],
- )
-
- private val switchToPreviousLanguageShortcut =
- shortcut("switch to previous language") {
- command(
- KeyEvent.META_SHIFT_ON,
- KeyEvent.KEYCODE_SPACE,
- )
- }
-
- private val switchToPreviousLanguageKeyboardShortcutInfo =
- KeyboardShortcutInfo(
- /* label = */ switchToPreviousLanguageShortcut.label,
- /* keycode = */ switchToPreviousLanguageShortcut.commands[0].keyCodes[1],
- /* modifiers = */ switchToPreviousLanguageShortcut.commands[0].keyCodes[0],
- )
-
- private val switchToPreviousLanguageShortcutAlternative =
- shortcut("switch to previous language") {
- command(
- KeyEvent.META_SHIFT_ON,
- KeyEvent.KEYCODE_SPACE,
- )
- }
-
- private val switchToPreviousLanguageKeyboardShortcutInfoAlternative =
- KeyboardShortcutInfo(
- /* label = */ switchToPreviousLanguageShortcutAlternative.label,
- /* keycode = */ switchToPreviousLanguageShortcutAlternative.commands[0].keyCodes[1],
- /* modifiers = */ switchToPreviousLanguageShortcutAlternative.commands[0].keyCodes[0],
- )
-
- private val showOnscreenKeyboardShortcut =
- shortcut(label = "Show on-screen keyboard") {
- command(KeyEvent.META_ALT_ON, KeyEvent.KEYCODE_K)
- }
-
- private val showOnScreenKeyboardShortcutInfo =
- KeyboardShortcutInfo(
- /* label = */ showOnscreenKeyboardShortcut.label,
- /* keycode = */ showOnscreenKeyboardShortcut.commands[0].keyCodes[1],
- /* modifiers = */ showOnscreenKeyboardShortcut.commands[0].keyCodes[0],
- )
-
- private val accessClipboardShortcut =
- shortcut(label = "Access clipboard") { command(KeyEvent.META_ALT_ON, KeyEvent.KEYCODE_V) }
-
- private val accessClipboardShortcutInfo =
- KeyboardShortcutInfo(
- /* label = */ accessClipboardShortcut.label,
- /* keycode = */ accessClipboardShortcut.commands[0].keyCodes[1],
- /* modifiers = */ accessClipboardShortcut.commands[0].keyCodes[0],
- )
-
- private val imeShortcutGroups =
- listOf(
- KeyboardShortcutGroup(
- /* label = */ "input",
- /* shortcutInfoList = */ listOf(
- switchToNextLanguageKeyboardShortcutInfo,
- switchToPreviousLanguageKeyboardShortcutInfo
- )
- )
- )
-
- private val imeShortcutCategory =
- ShortcutCategory(
- type = ShortcutCategoryType.IME,
- subCategories =
- listOf(
- ShortcutSubCategory(
- imeShortcutGroups[0].label.toString(),
- listOf(switchToNextLanguageShortcut, switchToPreviousLanguageShortcut)
- )
- )
- )
-
- private val imeShortcutsGroupsWithDuplicateLabels =
- listOf(
- KeyboardShortcutGroup(
- "input",
- listOf(
- switchToNextLanguageKeyboardShortcutInfo,
- switchToNextLanguageKeyboardShortcutInfoAlternative,
- switchToPreviousLanguageKeyboardShortcutInfo,
- switchToPreviousLanguageKeyboardShortcutInfoAlternative
- )
- ),
- KeyboardShortcutGroup(
- "Gboard",
- listOf(
- showOnScreenKeyboardShortcutInfo,
- accessClipboardShortcutInfo,
- )
- )
- )
-
- private val expectedGroupedShortcutCategories =
- ShortcutCategory(
- type = ShortcutCategoryType.IME,
- subCategories =
- listOf(
- ShortcutSubCategory(
- imeShortcutsGroupsWithDuplicateLabels[0].label.toString(),
- listOf(
- switchToNextLanguageShortcut.copy(
- commands =
- switchToNextLanguageShortcut.commands +
- switchToNextLanguageShortcutAlternative.commands
- ),
- switchToPreviousLanguageShortcut.copy(
- commands =
- switchToPreviousLanguageShortcut.commands +
- switchToPreviousLanguageShortcutAlternative.commands
- )
- ),
+ assertThat(categories)
+ .containsExactly(
+ TestShortcuts.systemCategory,
+ TestShortcuts.multitaskingCategory,
+ ShortcutCategory(
+ type = IME,
+ subCategories =
+ TestShortcuts.subCategoriesWithGroupedDuplicatedShortcutLabels
),
- ShortcutSubCategory(
- imeShortcutsGroupsWithDuplicateLabels[1].label.toString(),
- listOf(showOnscreenKeyboardShortcut, accessClipboardShortcut),
- )
)
- )
+ .inOrder()
+ }
+
+ @Test
+ fun categories_stateActive_systemShortcutsWithDuplicateLabels_emitsGroupedShortcuts() =
+ testScope.runTest {
+ systemShortcutsSource.setGroups(TestShortcuts.groupsWithDuplicateShortcutLabels)
+
+ val categories by collectLastValue(interactor.shortcutCategories)
+
+ helper.showFromActivity()
+
+ assertThat(categories)
+ .containsExactly(
+ ShortcutCategory(
+ type = SYSTEM,
+ subCategories =
+ TestShortcuts.subCategoriesWithGroupedDuplicatedShortcutLabels
+ ),
+ TestShortcuts.multitaskingCategory,
+ TestShortcuts.imeCategory,
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun categories_stateActive_multiTaskingShortcutsWithDuplicateLabels_emitsGroupedShortcuts() =
+ testScope.runTest {
+ multitaskingShortcutsSource.setGroups(TestShortcuts.groupsWithDuplicateShortcutLabels)
+
+ val categories by collectLastValue(interactor.shortcutCategories)
+
+ helper.showFromActivity()
+
+ assertThat(categories)
+ .containsExactly(
+ TestShortcuts.systemCategory,
+ ShortcutCategory(
+ type = MULTI_TASKING,
+ subCategories =
+ TestShortcuts.subCategoriesWithGroupedDuplicatedShortcutLabels
+ ),
+ TestShortcuts.imeCategory,
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun categories_stateActive_imeShortcutsWithUnsupportedModifiers_discardUnsupported() =
+ testScope.runTest {
+ helper.setImeShortcuts(TestShortcuts.groupsWithUnsupportedModifier)
+ val categories by collectLastValue(interactor.shortcutCategories)
+
+ helper.showFromActivity()
+
+ assertThat(categories)
+ .containsExactly(
+ TestShortcuts.systemCategory,
+ TestShortcuts.multitaskingCategory,
+ ShortcutCategory(
+ type = IME,
+ subCategories = TestShortcuts.subCategoriesWithUnsupportedModifiersRemoved
+ ),
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun categories_stateActive_systemShortcutsWithUnsupportedModifiers_discardUnsupported() =
+ testScope.runTest {
+ systemShortcutsSource.setGroups(TestShortcuts.groupsWithUnsupportedModifier)
+ val categories by collectLastValue(interactor.shortcutCategories)
+
+ helper.showFromActivity()
+
+ assertThat(categories)
+ .containsExactly(
+ ShortcutCategory(
+ type = SYSTEM,
+ subCategories = TestShortcuts.subCategoriesWithUnsupportedModifiersRemoved
+ ),
+ TestShortcuts.multitaskingCategory,
+ TestShortcuts.imeCategory,
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun categories_stateActive_multitaskingShortcutsWithUnsupportedModifiers_discardUnsupported() =
+ testScope.runTest {
+ multitaskingShortcutsSource.setGroups(TestShortcuts.groupsWithUnsupportedModifier)
+ val categories by collectLastValue(interactor.shortcutCategories)
+
+ helper.showFromActivity()
+
+ assertThat(categories)
+ .containsExactly(
+ TestShortcuts.systemCategory,
+ ShortcutCategory(
+ type = MULTI_TASKING,
+ subCategories = TestShortcuts.subCategoriesWithUnsupportedModifiersRemoved
+ ),
+ TestShortcuts.imeCategory,
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun categories_stateActive_imeShortcutsWitOnlyUnsupportedModifiers_discardsCategory() =
+ testScope.runTest {
+ helper.setImeShortcuts(TestShortcuts.groupsWithOnlyUnsupportedModifiers)
+ val categories by collectLastValue(interactor.shortcutCategories)
+
+ helper.showFromActivity()
+
+ assertThat(categories)
+ .containsExactly(
+ TestShortcuts.systemCategory,
+ TestShortcuts.multitaskingCategory,
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun categories_stateActive_systemShortcutsWitOnlyUnsupportedModifiers_discardsCategory() =
+ testScope.runTest {
+ systemShortcutsSource.setGroups(TestShortcuts.groupsWithOnlyUnsupportedModifiers)
+ val categories by collectLastValue(interactor.shortcutCategories)
+
+ helper.showFromActivity()
+
+ assertThat(categories)
+ .containsExactly(
+ TestShortcuts.multitaskingCategory,
+ TestShortcuts.imeCategory,
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun categories_stateActive_multitaskingShortcutsWitOnlyUnsupportedModifiers_discardsCategory() =
+ testScope.runTest {
+ multitaskingShortcutsSource.setGroups(TestShortcuts.groupsWithOnlyUnsupportedModifiers)
+ val categories by collectLastValue(interactor.shortcutCategories)
+
+ helper.showFromActivity()
+
+ assertThat(categories)
+ .containsExactly(
+ TestShortcuts.systemCategory,
+ TestShortcuts.imeCategory,
+ )
+ .inOrder()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt
index f32e775..c9871f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt
@@ -21,6 +21,9 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardSurfaceBehindModel
@@ -28,12 +31,18 @@
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.util.mockTopActivityClassName
import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.data.repository.Idle
+import com.android.systemui.scene.data.repository.Transition
+import com.android.systemui.scene.data.repository.setSceneTransition
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shared.system.ActivityManagerWrapper
import com.android.systemui.shared.system.activityManagerWrapper
import com.android.systemui.statusbar.notification.domain.interactor.notificationLaunchAnimationInteractor
import com.android.systemui.testKosmos
import com.android.systemui.util.assertValuesMatch
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -44,16 +53,22 @@
@kotlinx.coroutines.ExperimentalCoroutinesApi
class KeyguardSurfaceBehindInteractorTest : SysuiTestCase() {
private val kosmos = testKosmos()
- private val testScope = kosmos.testScope
- private val underTest = kosmos.keyguardSurfaceBehindInteractor
- private val transitionRepository = kosmos.fakeKeyguardTransitionRepository
- private val inWindowUnlockInteractor = kosmos.inWindowLauncherUnlockAnimationInteractor
- private val activityManagerWrapper = kosmos.activityManagerWrapper
+ private lateinit var testScope: TestScope
+ private lateinit var underTest: KeyguardSurfaceBehindInteractor
+ private lateinit var transitionRepository: FakeKeyguardTransitionRepository
+ private lateinit var inWindowUnlockInteractor: InWindowLauncherUnlockAnimationInteractor
+ private lateinit var activityManagerWrapper: ActivityManagerWrapper
private val LAUNCHER_ACTIVITY_NAME = "launcher"
@Before
fun setUp() {
+ testScope = kosmos.testScope
+ underTest = kosmos.keyguardSurfaceBehindInteractor
+ transitionRepository = kosmos.fakeKeyguardTransitionRepository
+ inWindowUnlockInteractor = kosmos.inWindowLauncherUnlockAnimationInteractor
+ activityManagerWrapper = kosmos.activityManagerWrapper
+
inWindowUnlockInteractor.setLauncherActivityClass(LAUNCHER_ACTIVITY_NAME)
// Default to having something other than Launcher on top.
@@ -61,6 +76,7 @@
}
@Test
+ @DisableSceneContainer
fun testSurfaceBehindModel_toAppSurface() =
testScope.runTest {
val values by collectValues(underTest.viewParams)
@@ -136,6 +152,7 @@
}
@Test
+ @DisableSceneContainer
fun testSurfaceBehindModel_toLauncher() =
testScope.runTest {
val values by collectValues(underTest.viewParams)
@@ -196,6 +213,7 @@
}
@Test
+ @DisableSceneContainer
fun testSurfaceBehindModel_fromNotificationLaunch() =
testScope.runTest {
val values by collectValues(underTest.viewParams)
@@ -230,6 +248,105 @@
}
@Test
+ @EnableSceneContainer
+ fun testSurfaceBehindModel_toAppSurface_scene_container() =
+ testScope.runTest {
+ val values by collectValues(underTest.viewParams)
+ runCurrent()
+
+ assertThat(values)
+ .containsExactly(
+ // We're initialized in LOCKSCREEN.
+ KeyguardSurfaceBehindModel(alpha = 0f),
+ )
+
+ kosmos.setSceneTransition(Transition(Scenes.Lockscreen, Scenes.Gone))
+
+ values.assertValuesMatch(
+ { it == KeyguardSurfaceBehindModel(alpha = 0f) },
+ // Once we start a transition to GONE, we should fade in and translate up. The exact
+ // start value depends on screen density, so just look for != 0.
+ {
+ it.animateFromAlpha == 0f &&
+ it.alpha == 1f &&
+ it.animateFromTranslationY != 0f &&
+ it.translationY == 0f
+ }
+ )
+
+ kosmos.setSceneTransition(Idle(Scenes.Gone))
+
+ values.assertValuesMatch(
+ { it == KeyguardSurfaceBehindModel(alpha = 0f) },
+ {
+ it.animateFromAlpha == 0f &&
+ it.alpha == 1f &&
+ it.animateFromTranslationY != 0f &&
+ it.translationY == 0f
+ },
+ // Once the current state is GONE, we should default to alpha = 1f.
+ { it == KeyguardSurfaceBehindModel(alpha = 1f) }
+ )
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun testSurfaceBehindModel_toLauncher_scene_container() =
+ testScope.runTest {
+ val values by collectValues(underTest.viewParams)
+ activityManagerWrapper.mockTopActivityClassName(LAUNCHER_ACTIVITY_NAME)
+ runCurrent()
+
+ assertThat(values)
+ .containsExactly(
+ // We're initialized in LOCKSCREEN.
+ KeyguardSurfaceBehindModel(alpha = 0f),
+ )
+ .inOrder()
+
+ kosmos.setSceneTransition(Transition(Scenes.Lockscreen, Scenes.Gone))
+
+ assertThat(values)
+ .containsExactly(
+ KeyguardSurfaceBehindModel(alpha = 0f),
+ // We should instantly set alpha = 1, with no animations, when Launcher is
+ // behind
+ // the keyguard since we're playing in-window animations.
+ KeyguardSurfaceBehindModel(alpha = 1f),
+ )
+ .inOrder()
+
+ kosmos.setSceneTransition(Idle(Scenes.Gone))
+
+ assertThat(values)
+ .containsExactly(
+ KeyguardSurfaceBehindModel(alpha = 0f),
+ // Should have remained at alpha = 1f through the entire animation.
+ KeyguardSurfaceBehindModel(alpha = 1f),
+ )
+ .inOrder()
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun testSurfaceBehindModel_fromNotificationLaunch_scene_container() =
+ testScope.runTest {
+ val values by collectValues(underTest.viewParams)
+ runCurrent()
+
+ kosmos.notificationLaunchAnimationInteractor.setIsLaunchAnimationRunning(true)
+ runCurrent()
+
+ kosmos.setSceneTransition(Transition(Scenes.Lockscreen, Scenes.Gone))
+
+ values.assertValuesMatch(
+ // We should be at alpha = 0f during the animation.
+ { it == KeyguardSurfaceBehindModel(alpha = 0f) },
+ )
+ }
+
+ @Test
+ @DisableSceneContainer
fun notificationLaunchFromLockscreen_isAnimatingSurfaceTrue() =
testScope.runTest {
val isAnimatingSurface by collectLastValue(underTest.isAnimatingSurface)
@@ -253,6 +370,7 @@
}
@Test
+ @DisableSceneContainer
fun notificationLaunchFromGone_isAnimatingSurfaceFalse() =
testScope.runTest {
val isAnimatingSurface by collectLastValue(underTest.isAnimatingSurface)
@@ -276,6 +394,7 @@
}
@Test
+ @DisableSceneContainer
fun notificationLaunchFalse_isAnimatingSurfaceFalse() =
testScope.runTest {
val isAnimatingSurface by collectLastValue(underTest.isAnimatingSurface)
@@ -297,4 +416,44 @@
runCurrent()
assertThat(isAnimatingSurface).isFalse()
}
+
+ @Test
+ @EnableSceneContainer
+ fun notificationLaunchFromLockscreen_isAnimatingSurfaceTrue_scene_container() =
+ testScope.runTest {
+ val isAnimatingSurface by collectLastValue(underTest.isAnimatingSurface)
+
+ kosmos.setSceneTransition(Idle(Scenes.Lockscreen))
+ kosmos.notificationLaunchAnimationInteractor.setIsLaunchAnimationRunning(true)
+ runCurrent()
+
+ assertThat(isAnimatingSurface).isTrue()
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun notificationLaunchFromGone_isAnimatingSurfaceFalse_scene_container() =
+ testScope.runTest {
+ val isAnimatingSurface by collectLastValue(underTest.isAnimatingSurface)
+
+ kosmos.setSceneTransition(Idle(Scenes.Gone))
+ kosmos.notificationLaunchAnimationInteractor.setIsLaunchAnimationRunning(true)
+ runCurrent()
+ assertThat(isAnimatingSurface).isFalse()
+
+ kosmos.setSceneTransition(Transition(from = Scenes.Gone, to = Scenes.Lockscreen))
+ assertThat(isAnimatingSurface).isFalse()
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun notificationLaunchFalse_isAnimatingSurfaceFalse_scene_container() =
+ testScope.runTest {
+ val isAnimatingSurface by collectLastValue(underTest.isAnimatingSurface)
+
+ kosmos.setSceneTransition(Idle(Scenes.Lockscreen))
+ kosmos.notificationLaunchAnimationInteractor.setIsLaunchAnimationRunning(false)
+ runCurrent()
+ assertThat(isAnimatingSurface).isFalse()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index 246cfbf..824132a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -30,7 +30,6 @@
import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.communal.domain.interactor.setCommunalAvailable
import com.android.systemui.communal.shared.model.CommunalScenes
-import com.android.systemui.dock.fakeDockManager
import com.android.systemui.flags.BrokenWithSceneContainer
import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.FakeFeatureFlags
@@ -124,7 +123,6 @@
private val powerInteractor by lazy { kosmos.powerInteractor }
private val communalInteractor by lazy { kosmos.communalInteractor }
- private val dockManager by lazy { kosmos.fakeDockManager }
companion object {
@JvmStatic
@@ -583,6 +581,7 @@
}
@Test
+ @DisableSceneContainer
fun dozingToPrimaryBouncer() =
testScope.runTest {
// GIVEN a prior transition has run to DOZING
@@ -597,8 +596,8 @@
assertThat(transitionRepository)
.startedTransition(
- to = KeyguardState.PRIMARY_BOUNCER,
from = KeyguardState.DOZING,
+ to = KeyguardState.PRIMARY_BOUNCER,
animatorAssertion = { it.isNotNull() }
)
@@ -633,6 +632,7 @@
}
@Test
+ @DisableSceneContainer
fun dozingToGlanceableHub() =
testScope.runTest {
// GIVEN a prior transition has run to DOZING
@@ -654,8 +654,8 @@
assertThat(transitionRepository)
.startedTransition(
- to = KeyguardState.GLANCEABLE_HUB,
from = KeyguardState.DOZING,
+ to = KeyguardState.GLANCEABLE_HUB,
animatorAssertion = { it.isNotNull() }
)
@@ -1452,6 +1452,7 @@
}
@Test
+ @DisableSceneContainer
fun dreamingToPrimaryBouncer() =
testScope.runTest {
// GIVEN a prior transition has run to DREAMING
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
index f624f20..b7fb759 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
@@ -57,7 +57,9 @@
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.anyString
+import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoSession
import org.mockito.quality.Strictness
@@ -75,6 +77,9 @@
private lateinit var mockitoSession: MockitoSession
+ private val spiedContext = spy(context)
+ private val spiedResources = spy(spiedContext.resources)
+
@Before
fun setUp() {
mockitoSession =
@@ -100,6 +105,8 @@
)
)
.thenReturn(listOf("com.google.test.notes"))
+
+ `when`(spiedContext.resources).thenReturn(spiedResources)
}
@After
@@ -109,7 +116,7 @@
private fun createUnderTest(isEnabled: Boolean = true): KeyguardQuickAffordanceConfig =
NoteTaskQuickAffordanceConfig(
- context = context,
+ context = spiedContext,
controller = controller,
stylusManager = stylusManager,
userManager = userManager,
@@ -132,126 +139,262 @@
)
// region lockScreenState
+ @Suppress("ktlint:standard:max-line-length")
@Test
- fun lockScreenState_stylusUsed_userUnlocked_isSelected_shouldEmitVisible() = runTest {
- val underTest = createUnderTest()
- TestConfig().setStylusEverUsed(true).setUserUnlocked(true).setConfigSelections(underTest)
-
- val actual by collectLastValue(underTest.lockScreenState)
-
- assertThat(actual).isEqualTo(createLockScreenStateVisible())
- }
-
- @Test
- fun lockScreenState_stylusUsed_userUnlocked_isSelected_noDefaultNotesAppSet_shouldEmitHidden() =
+ fun lockScreenState_stylusUnused_userLocked_customizationDisabled_notesLockScreenShortcutNotSelected_shouldEmitHidden() =
runTest {
val underTest = createUnderTest()
TestConfig()
- .setStylusEverUsed(true)
- .setUserUnlocked(true)
- .setConfigSelections(underTest)
- whenever(
- roleManager.getRoleHoldersAsUser(
- eq(RoleManager.ROLE_NOTES),
- any(UserHandle::class.java)
- )
- )
- .thenReturn(emptyList())
+ .setStylusEverUsed(false)
+ .setUserUnlocked(false)
+ .setLockScreenCustomizationEnabled(false)
+ .setConfigSelections()
val actual by collectLastValue(underTest.lockScreenState)
assertThat(actual).isEqualTo(LockScreenState.Hidden)
}
+ @Suppress("ktlint:standard:max-line-length")
@Test
- fun lockScreenState_stylusUnused_userUnlocked_isSelected_shouldEmitHidden() = runTest {
- val underTest = createUnderTest()
- TestConfig().setStylusEverUsed(false).setUserUnlocked(true).setConfigSelections(underTest)
+ fun lockScreenState_stylusUnused_userLocked_customizationDisabled_notesLockScreenShortcutSelected_shouldEmitHidden() =
+ runTest {
+ val underTest = createUnderTest()
+ TestConfig()
+ .setStylusEverUsed(false)
+ .setUserUnlocked(false)
+ .setLockScreenCustomizationEnabled(false)
+ .setConfigSelections(underTest)
- val actual by collectLastValue(underTest.lockScreenState)
+ val actual by collectLastValue(underTest.lockScreenState)
- assertThat(actual).isEqualTo(LockScreenState.Hidden)
- }
+ assertThat(actual).isEqualTo(LockScreenState.Hidden)
+ }
+ @Suppress("ktlint:standard:max-line-length")
@Test
- fun lockScreenState_stylusUsed_userLocked_isSelected_shouldEmitHidden() = runTest {
- val underTest = createUnderTest()
- TestConfig().setStylusEverUsed(true).setUserUnlocked(false).setConfigSelections(underTest)
+ fun lockScreenState_stylusUnused_userLocked_customizationEnabled_notesLockScreenShortcutNotSelected_shouldEmitHidden() =
+ runTest {
+ val underTest = createUnderTest()
+ TestConfig()
+ .setStylusEverUsed(false)
+ .setUserUnlocked(false)
+ .setLockScreenCustomizationEnabled(true)
+ .setConfigSelections()
- val actual by collectLastValue(underTest.lockScreenState)
+ val actual by collectLastValue(underTest.lockScreenState)
- assertThat(actual).isEqualTo(LockScreenState.Hidden)
- }
+ assertThat(actual).isEqualTo(LockScreenState.Hidden)
+ }
+ @Suppress("ktlint:standard:max-line-length")
@Test
- fun lockScreenState_stylusUsed_userUnlocked_noSelected_shouldEmitHidden() = runTest {
- TestConfig().setStylusEverUsed(true).setUserUnlocked(true).setConfigSelections()
+ fun lockScreenState_stylusUnused_userLocked_customizationEnabled_notesLockScreenShortcutSelected_shouldEmitHidden() =
+ runTest {
+ val underTest = createUnderTest()
+ TestConfig()
+ .setStylusEverUsed(false)
+ .setUserUnlocked(false)
+ .setLockScreenCustomizationEnabled(true)
+ .setConfigSelections(underTest)
- val underTest = createUnderTest()
- val actual by collectLastValue(underTest.lockScreenState)
+ val actual by collectLastValue(underTest.lockScreenState)
- assertThat(actual).isEqualTo(LockScreenState.Hidden)
- }
+ assertThat(actual).isEqualTo(LockScreenState.Hidden)
+ }
+ @Suppress("ktlint:standard:max-line-length")
@Test
- fun lockScreenState_stylusUnused_userUnlocked_noSelected_shouldEmitHidden() = runTest {
- TestConfig().setStylusEverUsed(false).setUserUnlocked(true).setConfigSelections()
+ fun lockScreenState_stylusUnused_userUnlocked_customizationDisabled_notesLockScreenShortcutNotSelected_shouldEmitHidden() =
+ runTest {
+ val underTest = createUnderTest()
+ TestConfig()
+ .setStylusEverUsed(false)
+ .setUserUnlocked(true)
+ .setLockScreenCustomizationEnabled(false)
+ .setConfigSelections()
- val underTest = createUnderTest()
- val actual by collectLastValue(underTest.lockScreenState)
+ val actual by collectLastValue(underTest.lockScreenState)
- assertThat(actual).isEqualTo(LockScreenState.Hidden)
- }
+ assertThat(actual).isEqualTo(LockScreenState.Hidden)
+ }
+ @Suppress("ktlint:standard:max-line-length")
@Test
- fun lockScreenState_stylusUsed_userLocked_noSelected_shouldEmitHidden() = runTest {
- TestConfig().setStylusEverUsed(true).setUserUnlocked(false).setConfigSelections()
+ fun lockScreenState_stylusUnused_userUnlocked_customizationDisabled_notesLockScreenShortcutSelected_shouldEmitHidden() =
+ runTest {
+ val underTest = createUnderTest()
+ TestConfig()
+ .setStylusEverUsed(false)
+ .setUserUnlocked(true)
+ .setLockScreenCustomizationEnabled(false)
+ .setConfigSelections(underTest)
- val underTest = createUnderTest()
- val actual by collectLastValue(underTest.lockScreenState)
+ val actual by collectLastValue(underTest.lockScreenState)
- assertThat(actual).isEqualTo(LockScreenState.Hidden)
- }
+ assertThat(actual).isEqualTo(LockScreenState.Hidden)
+ }
+ @Suppress("ktlint:standard:max-line-length")
@Test
- fun lockScreenState_stylusUsed_userUnlocked_customSelections_shouldEmitHidden() = runTest {
- TestConfig().setStylusEverUsed(true).setUserUnlocked(true).setConfigSelections(mock())
+ fun lockScreenState_stylusUnused_userUnlocked_customizationEnabled_notesLockScreenShortcutNotSelected_shouldEmitHidden() =
+ runTest {
+ val underTest = createUnderTest()
+ TestConfig()
+ .setStylusEverUsed(false)
+ .setUserUnlocked(true)
+ .setLockScreenCustomizationEnabled(true)
+ .setConfigSelections()
- val underTest = createUnderTest()
- val actual by collectLastValue(underTest.lockScreenState)
+ val actual by collectLastValue(underTest.lockScreenState)
- assertThat(actual).isEqualTo(LockScreenState.Hidden)
- }
+ assertThat(actual).isEqualTo(LockScreenState.Hidden)
+ }
+ @Suppress("ktlint:standard:max-line-length")
@Test
- fun lockScreenState_stylusUnused_userUnlocked_customSelections_shouldEmitHidden() = runTest {
- TestConfig().setStylusEverUsed(false).setUserUnlocked(true).setConfigSelections(mock())
+ fun lockScreenState_stylusUnused_userUnlocked_customizationEnabled_notesLockScreenShortcutSelected_shouldEmitVisible() =
+ runTest {
+ val underTest = createUnderTest()
+ TestConfig()
+ .setStylusEverUsed(false)
+ .setUserUnlocked(true)
+ .setLockScreenCustomizationEnabled(true)
+ .setConfigSelections(underTest)
- val underTest = createUnderTest()
- val actual by collectLastValue(underTest.lockScreenState)
+ val actual by collectLastValue(underTest.lockScreenState)
- assertThat(actual).isEqualTo(LockScreenState.Hidden)
- }
+ assertThat(actual).isEqualTo(createLockScreenStateVisible())
+ }
+ @Suppress("ktlint:standard:max-line-length")
@Test
- fun lockScreenState_stylusUsed_userLocked_customSelections_shouldEmitHidden() = runTest {
- TestConfig().setStylusEverUsed(true).setUserUnlocked(false).setConfigSelections(mock())
+ fun lockScreenState_stylusUsed_userLocked_customizationDisabled_notesLockScreenShortcutNotSelected_shouldEmitHidden() =
+ runTest {
+ val underTest = createUnderTest()
+ TestConfig()
+ .setStylusEverUsed(true)
+ .setUserUnlocked(false)
+ .setLockScreenCustomizationEnabled(false)
+ .setConfigSelections()
- val underTest = createUnderTest()
- val actual by collectLastValue(underTest.lockScreenState)
+ val actual by collectLastValue(underTest.lockScreenState)
- assertThat(actual).isEqualTo(LockScreenState.Hidden)
- }
+ assertThat(actual).isEqualTo(LockScreenState.Hidden)
+ }
+ @Suppress("ktlint:standard:max-line-length")
@Test
- fun lockScreenState_isNotEnabled_shouldEmitHidden() = runTest {
- TestConfig().setStylusEverUsed(true).setUserUnlocked(true).setConfigSelections()
+ fun lockScreenState_stylusUsed_userLocked_customizationDisabled_notesLockScreenShortcutSelected_shouldEmitHidden() =
+ runTest {
+ val underTest = createUnderTest()
+ TestConfig()
+ .setStylusEverUsed(true)
+ .setUserUnlocked(false)
+ .setLockScreenCustomizationEnabled(false)
+ .setConfigSelections(underTest)
- val underTest = createUnderTest(isEnabled = false)
- val actual by collectLastValue(underTest.lockScreenState)
+ val actual by collectLastValue(underTest.lockScreenState)
- assertThat(actual).isEqualTo(LockScreenState.Hidden)
- }
+ assertThat(actual).isEqualTo(LockScreenState.Hidden)
+ }
+
+ @Suppress("ktlint:standard:max-line-length")
+ @Test
+ fun lockScreenState_stylusUsed_userLocked_customizationEnabled_notesLockScreenShortcutNotSelected_shouldEmitHidden() =
+ runTest {
+ val underTest = createUnderTest()
+ TestConfig()
+ .setStylusEverUsed(true)
+ .setUserUnlocked(false)
+ .setLockScreenCustomizationEnabled(true)
+ .setConfigSelections()
+
+ val actual by collectLastValue(underTest.lockScreenState)
+
+ assertThat(actual).isEqualTo(LockScreenState.Hidden)
+ }
+
+ @Suppress("ktlint:standard:max-line-length")
+ @Test
+ fun lockScreenState_stylusUsed_userLocked_customizationEnabled_notesLockScreenShortcutSelected_shouldEmitHidden() =
+ runTest {
+ val underTest = createUnderTest()
+ TestConfig()
+ .setStylusEverUsed(true)
+ .setUserUnlocked(false)
+ .setLockScreenCustomizationEnabled(true)
+ .setConfigSelections(underTest)
+
+ val actual by collectLastValue(underTest.lockScreenState)
+
+ assertThat(actual).isEqualTo(LockScreenState.Hidden)
+ }
+
+ @Suppress("ktlint:standard:max-line-length")
+ @Test
+ fun lockScreenState_stylusUsed_userUnlocked_customizationDisabled_notesLockScreenShortcutNotSelected_shouldEmitVisible() =
+ runTest {
+ val underTest = createUnderTest()
+ TestConfig()
+ .setStylusEverUsed(true)
+ .setUserUnlocked(true)
+ .setLockScreenCustomizationEnabled(false)
+ .setConfigSelections()
+
+ val actual by collectLastValue(underTest.lockScreenState)
+
+ assertThat(actual).isEqualTo(createLockScreenStateVisible())
+ }
+
+ @Suppress("ktlint:standard:max-line-length")
+ @Test
+ fun lockScreenState_stylusUsed_userUnlocked_customizationDisabled_notesLockScreenShortcutSelected_shouldEmitVisible() =
+ runTest {
+ val underTest = createUnderTest()
+ TestConfig()
+ .setStylusEverUsed(true)
+ .setUserUnlocked(true)
+ .setLockScreenCustomizationEnabled(false)
+ .setConfigSelections(underTest)
+
+ val actual by collectLastValue(underTest.lockScreenState)
+
+ assertThat(actual).isEqualTo(createLockScreenStateVisible())
+ }
+
+ @Suppress("ktlint:standard:max-line-length")
+ @Test
+ fun lockScreenState_stylusUsed_userUnlocked_customizationEnabled_notesLockScreenShortcutNotSelected_shouldEmitHidden() =
+ runTest {
+ val underTest = createUnderTest()
+ TestConfig()
+ .setStylusEverUsed(true)
+ .setUserUnlocked(true)
+ .setLockScreenCustomizationEnabled(true)
+ .setConfigSelections()
+
+ val actual by collectLastValue(underTest.lockScreenState)
+
+ assertThat(actual).isEqualTo(LockScreenState.Hidden)
+ }
+
+ @Suppress("ktlint:standard:max-line-length")
+ @Test
+ fun lockScreenState_stylusUsed_userUnlocked_customizationEnabled_notesLockScreenShortcutSelected_shouldEmitVisible() =
+ runTest {
+ val underTest = createUnderTest()
+ TestConfig()
+ .setStylusEverUsed(true)
+ .setUserUnlocked(true)
+ .setLockScreenCustomizationEnabled(true)
+ .setConfigSelections(underTest)
+
+ val actual by collectLastValue(underTest.lockScreenState)
+
+ assertThat(actual).isEqualTo(createLockScreenStateVisible())
+ }
+
// endregion
@Test
@@ -294,18 +437,24 @@
.isEqualTo(ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE)
assertThat(disabled.actionIntent?.`package`).isEqualTo(context.packageName)
}
+
// endregion
private inner class TestConfig {
fun setStylusEverUsed(value: Boolean) = also {
- whenever(InputSettings.isStylusEverUsed(mContext)).thenReturn(value)
+ whenever(InputSettings.isStylusEverUsed(spiedContext)).thenReturn(value)
}
fun setUserUnlocked(value: Boolean) = also {
whenever(userManager.isUserUnlocked).thenReturn(value)
}
+ fun setLockScreenCustomizationEnabled(value: Boolean) = also {
+ `when`(spiedResources.getBoolean(R.bool.custom_lockscreen_shortcuts_enabled))
+ .thenReturn(value)
+ }
+
fun setConfigSelections(vararg values: KeyguardQuickAffordanceConfig) = also {
val slotKey = "bottom-right"
val configSnapshots = values.toList()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index e7ca091..b80d1a4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -419,7 +419,7 @@
mShadeAnimationInteractor = new ShadeAnimationInteractorLegacyImpl(
new ShadeAnimationRepository(), mShadeRepository);
mPowerInteractor = keyguardInteractorDeps.getPowerInteractor();
- when(mKeyguardTransitionInteractor.isInTransitionToStateWhere(any())).thenReturn(
+ when(mKeyguardTransitionInteractor.isInTransitionWhere(any(), any())).thenReturn(
MutableStateFlow(false));
when(mKeyguardTransitionInteractor.isInTransition(any(), any()))
.thenReturn(emptyFlow());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index 5c45b2e..3669e3d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -64,6 +64,10 @@
@Mock private SectionHeaderController mPeopleHeaderController;
@Mock private SectionHeaderController mAlertingHeaderController;
@Mock private SectionHeaderController mSilentHeaderController;
+ @Mock private SectionHeaderController mNewsHeaderController;
+ @Mock private SectionHeaderController mSocialHeaderController;
+ @Mock private SectionHeaderController mRecsHeaderController;
+ @Mock private SectionHeaderController mPromoHeaderController;
private NotificationSectionsManager mSectionsManager;
@@ -94,7 +98,11 @@
mIncomingHeaderController,
mPeopleHeaderController,
mAlertingHeaderController,
- mSilentHeaderController
+ mSilentHeaderController,
+ mNewsHeaderController,
+ mSocialHeaderController,
+ mRecsHeaderController,
+ mPromoHeaderController
);
// Required in order for the header inflation to work properly
when(mNssl.generateLayoutParams(any(AttributeSet.class)))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 770c424..a925ccf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -49,6 +49,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.annotation.DimenRes;
import android.graphics.Insets;
import android.graphics.Rect;
import android.os.SystemClock;
@@ -138,7 +139,7 @@
@Mock private NotificationStackScrollLayoutController mStackScrollLayoutController;
@Mock private ScreenOffAnimationController mScreenOffAnimationController;
@Mock private NotificationShelf mNotificationShelf;
- @Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator;
+ @Mock private NotificationStackSizeCalculator mStackSizeCalculator;
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Mock private LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
@Mock private AvalancheController mAvalancheController;
@@ -197,7 +198,7 @@
// refer to the CUT's member variables, not the spy's member variables.
mStackScrollerInternal = new NotificationStackScrollLayout(getContext(), null);
mStackScrollerInternal.initView(getContext(), mNotificationSwipeHelper,
- mNotificationStackSizeCalculator);
+ mStackSizeCalculator);
mStackScroller = spy(mStackScrollerInternal);
mStackScroller.setResetUserExpandedStatesRunnable(() -> {});
mStackScroller.setEmptyShadeView(mEmptyShadeView);
@@ -233,6 +234,32 @@
}
@Test
+ @EnableSceneContainer
+ public void testIntrinsicStackHeight() {
+ int stackHeight = 300;
+ when(mStackSizeCalculator.computeHeight(eq(mStackScroller), anyInt(), anyFloat()))
+ .thenReturn((float) stackHeight);
+
+ mStackScroller.updateContentHeight();
+
+ assertThat(mStackScroller.getIntrinsicStackHeight()).isEqualTo(stackHeight);
+ }
+
+ @Test
+ @DisableSceneContainer
+ public void testIntrinsicStackHeight_includesTopScrimPadding() {
+ int stackHeight = 300;
+ int topScrimPadding = px(R.dimen.notification_side_paddings);
+ when(mStackSizeCalculator.computeHeight(eq(mStackScroller), anyInt(), anyFloat()))
+ .thenReturn((float) stackHeight);
+
+ mStackScroller.updateContentHeight();
+
+ assertThat(mStackScroller.getIntrinsicStackHeight())
+ .isEqualTo(stackHeight + topScrimPadding);
+ }
+
+ @Test
@DisableSceneContainer // TODO(b/312473478): address disabled test
public void testUpdateStackHeight_qsExpansionZero() {
final float expansionFraction = 0.2f;
@@ -819,7 +846,7 @@
@DisableSceneContainer // TODO(b/312473478): address disabled test
public void setFractionToShade_recomputesStackHeight() {
mStackScroller.setFractionToShade(1f);
- verify(mNotificationStackSizeCalculator).computeHeight(any(), anyInt(), anyFloat());
+ verify(mStackSizeCalculator).computeHeight(any(), anyInt(), anyFloat());
}
@Test
@@ -1231,6 +1258,10 @@
assertEquals(expected, mAmbientState.isClearAllInProgress());
}
+ private int px(@DimenRes int id) {
+ return mTestableResources.getResources().getDimensionPixelSize(id);
+ }
+
private static void mockBoundsOnScreen(View view, Rect bounds) {
doAnswer(invocation -> {
Rect out = invocation.getArgument(0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index b12c098..c2a7b52 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -115,6 +115,17 @@
}
@Test
+ @EnableSceneContainer
+ fun resetViewStates_childPositionedAtStackTop() {
+ val stackTop = 100f
+ ambientState.stackTop = stackTop
+
+ stackScrollAlgorithm.resetViewStates(ambientState, 0)
+
+ assertThat(notificationRow.viewState.yTranslation).isEqualTo(stackTop)
+ }
+
+ @Test
fun resetViewStates_defaultHun_yTranslationIsInset() {
whenever(notificationRow.isPinned).thenReturn(true)
whenever(notificationRow.isHeadsUp).thenReturn(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index 0cb28cb..68e17c1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -316,6 +316,7 @@
}
@Test
+ @DisableSceneContainer
public void setBatteryListening_true_callbackAdded() {
mController.setBatteryListening(true);
@@ -323,6 +324,7 @@
}
@Test
+ @DisableSceneContainer
public void setBatteryListening_false_callbackRemoved() {
// First set to true so that we know setting to false is a change in state.
mController.setBatteryListening(true);
@@ -333,6 +335,7 @@
}
@Test
+ @DisableSceneContainer
public void setBatteryListening_trueThenTrue_callbackAddedOnce() {
mController.setBatteryListening(true);
mController.setBatteryListening(true);
@@ -372,6 +375,7 @@
}
@Test
+ @DisableSceneContainer
public void updateViewState_alphaAndVisibilityGiven_viewUpdated() {
// Verify the initial values so we know the method triggers changes.
assertThat(mKeyguardStatusBarView.getAlpha()).isEqualTo(1f);
@@ -386,6 +390,7 @@
}
@Test
+ @DisableSceneContainer
public void updateViewState_paramVisibleButIsDisabled_viewIsInvisible() {
mController.onViewAttached();
setDisableSystemIcons(true);
@@ -397,6 +402,7 @@
}
@Test
+ @DisableSceneContainer
public void updateViewState_notKeyguardState_nothingUpdated() {
mController.onViewAttached();
updateStateToNotKeyguard();
@@ -409,6 +415,7 @@
}
@Test
+ @DisableSceneContainer
public void updateViewState_bypassEnabledAndShouldListenForFace_viewHidden() {
mController.onViewAttached();
updateStateToKeyguard();
@@ -424,6 +431,7 @@
}
@Test
+ @DisableSceneContainer
public void updateViewState_bypassNotEnabled_viewShown() {
mController.onViewAttached();
updateStateToKeyguard();
@@ -438,6 +446,7 @@
}
@Test
+ @DisableSceneContainer
public void updateViewState_shouldNotListenForFace_viewShown() {
mController.onViewAttached();
updateStateToKeyguard();
@@ -452,6 +461,7 @@
}
@Test
+ @DisableSceneContainer
public void updateViewState_panelExpandedHeightZero_viewHidden() {
mController.onViewAttached();
updateStateToKeyguard();
@@ -464,6 +474,7 @@
}
@Test
+ @DisableSceneContainer
public void updateViewState_dragProgressOne_viewHidden() {
mController.onViewAttached();
updateStateToKeyguard();
@@ -476,6 +487,7 @@
}
@Test
+ @DisableSceneContainer
public void updateViewState_disableSystemInfoFalse_viewShown() {
mController.onViewAttached();
updateStateToKeyguard();
@@ -487,6 +499,7 @@
}
@Test
+ @DisableSceneContainer
public void updateViewState_disableSystemInfoTrue_viewHidden() {
mController.onViewAttached();
updateStateToKeyguard();
@@ -498,6 +511,7 @@
}
@Test
+ @DisableSceneContainer
public void updateViewState_disableSystemIconsFalse_viewShown() {
mController.onViewAttached();
updateStateToKeyguard();
@@ -509,6 +523,7 @@
}
@Test
+ @DisableSceneContainer
public void updateViewState_disableSystemIconsTrue_viewHidden() {
mController.onViewAttached();
updateStateToKeyguard();
@@ -608,6 +623,7 @@
}
@Test
+ @DisableSceneContainer
public void setAlpha_explicitAlpha_setsExplicitAlpha() {
mController.onViewAttached();
updateStateToKeyguard();
@@ -618,6 +634,7 @@
}
@Test
+ @DisableSceneContainer
public void setAlpha_explicitAlpha_thenMinusOneAlpha_setsAlphaBasedOnDefaultCriteria() {
mController.onViewAttached();
updateStateToKeyguard();
@@ -632,6 +649,7 @@
// TODO(b/195442899): Add more tests for #updateViewState once CLs are finalized.
@Test
+ @DisableSceneContainer
public void updateForHeadsUp_headsUpShouldBeVisible_viewHidden() {
mController.onViewAttached();
updateStateToKeyguard();
@@ -644,6 +662,7 @@
}
@Test
+ @DisableSceneContainer
public void updateForHeadsUp_headsUpShouldNotBeVisible_viewShown() {
mController.onViewAttached();
updateStateToKeyguard();
@@ -733,6 +752,7 @@
}
@Test
+ @DisableSceneContainer
public void animateKeyguardStatusBarIn_isDisabled_viewStillHidden() {
mController.onViewAttached();
updateStateToKeyguard();
diff --git a/packages/SystemUI/tests/utils/src/android/hardware/input/FakeInputManager.kt b/packages/SystemUI/tests/utils/src/android/hardware/input/FakeInputManager.kt
index 36ac4a4..c4f93d1 100644
--- a/packages/SystemUI/tests/utils/src/android/hardware/input/FakeInputManager.kt
+++ b/packages/SystemUI/tests/utils/src/android/hardware/input/FakeInputManager.kt
@@ -18,6 +18,7 @@
import android.view.InputDevice
import android.view.KeyCharacterMap
+import android.view.KeyCharacterMap.VIRTUAL_KEYBOARD
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import org.mockito.ArgumentMatchers.anyInt
@@ -25,7 +26,18 @@
class FakeInputManager {
- private val devices = mutableMapOf<Int, InputDevice>()
+ private val keyCharacterMap = KeyCharacterMap.load(VIRTUAL_KEYBOARD)
+
+ private val virtualKeyboard =
+ InputDevice.Builder()
+ .setId(VIRTUAL_KEYBOARD)
+ .setKeyboardType(InputDevice.KEYBOARD_TYPE_ALPHABETIC)
+ .setSources(InputDevice.SOURCE_KEYBOARD)
+ .setEnabled(true)
+ .setKeyCharacterMap(keyCharacterMap)
+ .build()
+
+ private val devices = mutableMapOf<Int, InputDevice>(VIRTUAL_KEYBOARD to virtualKeyboard)
val inputManager =
mock<InputManager> {
@@ -56,10 +68,6 @@
addKeyboard(id, enabled)
}
- fun addVirtualKeyboard(enabled: Boolean = true) {
- addKeyboard(id = KeyCharacterMap.VIRTUAL_KEYBOARD, enabled)
- }
-
private fun addKeyboard(id: Int, enabled: Boolean = true) {
devices[id] =
InputDevice.Builder()
@@ -67,6 +75,7 @@
.setKeyboardType(InputDevice.KEYBOARD_TYPE_ALPHABETIC)
.setSources(InputDevice.SOURCE_KEYBOARD)
.setEnabled(enabled)
+ .setKeyCharacterMap(keyCharacterMap)
.build()
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
index 9dae44d..7c53639 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
@@ -18,6 +18,7 @@
import android.app.ActivityManager
import android.app.admin.DevicePolicyManager
import android.app.trust.TrustManager
+import android.hardware.fingerprint.FingerprintManager
import android.os.UserManager
import android.service.notification.NotificationListenerService
import android.util.DisplayMetrics
@@ -94,6 +95,7 @@
@get:Provides val deviceProvisionedController: DeviceProvisionedController = mock(),
@get:Provides val dozeParameters: DozeParameters = mock(),
@get:Provides val dumpManager: DumpManager = mock(),
+ @get:Provides val fingerprintManager: FingerprintManager = mock(),
@get:Provides val headsUpManager: HeadsUpManager = mock(),
@get:Provides val guestResumeSessionReceiver: GuestResumeSessionReceiver = mock(),
@get:Provides val keyguardBypassController: KeyguardBypassController = mock(),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorKosmos.kt
index cbfc768..ae592b9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorKosmos.kt
@@ -17,17 +17,20 @@
package com.android.systemui.biometrics.domain.interactor
import android.content.applicationContext
+import android.hardware.fingerprint.FingerprintManager
import com.android.systemui.biometrics.authController
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.user.domain.interactor.selectedUserInteractor
+import com.android.systemui.util.mockito.mock
val Kosmos.udfpsOverlayInteractor by Fixture {
UdfpsOverlayInteractor(
context = applicationContext,
authController = authController,
selectedUserInteractor = selectedUserInteractor,
+ fingerprintManager = mock<FingerprintManager>(),
scope = applicationCoroutineScope,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
index 385a6dc..dab70f8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
@@ -59,10 +59,13 @@
val Kosmos.shortcutHelperCategoriesRepository by
Kosmos.Fixture {
ShortcutHelperCategoriesRepository(
+ applicationContext,
+ testDispatcher,
shortcutHelperSystemShortcutsSource,
shortcutHelperMultiTaskingShortcutsSource,
windowManager,
- shortcutHelperStateRepository
+ fakeInputManager.inputManager,
+ shortcutHelperStateRepository,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorKosmos.kt
index 78a419f..ce317d4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorKosmos.kt
@@ -32,6 +32,7 @@
FromAlternateBouncerTransitionInteractor(
transitionRepository = keyguardTransitionRepository,
transitionInteractor = keyguardTransitionInteractor,
+ internalTransitionInteractor = internalKeyguardTransitionInteractor,
scope = applicationCoroutineScope,
bgDispatcher = testDispatcher,
mainDispatcher = testDispatcher,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt
index 42af25e..ae138c8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt
@@ -29,6 +29,7 @@
FromAodTransitionInteractor(
transitionRepository = fakeKeyguardTransitionRepository,
transitionInteractor = keyguardTransitionInteractor,
+ internalTransitionInteractor = internalKeyguardTransitionInteractor,
scope = applicationCoroutineScope,
bgDispatcher = testDispatcher,
mainDispatcher = testDispatcher,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt
index edf77a0..e7e007f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt
@@ -30,6 +30,7 @@
FromDozingTransitionInteractor(
transitionRepository = keyguardTransitionRepository,
transitionInteractor = keyguardTransitionInteractor,
+ internalTransitionInteractor = internalKeyguardTransitionInteractor,
scope = applicationCoroutineScope,
bgDispatcher = testDispatcher,
mainDispatcher = testDispatcher,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractorKosmos.kt
index f7a9d59..7ebef10 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractorKosmos.kt
@@ -28,6 +28,7 @@
FromDreamingLockscreenHostedTransitionInteractor(
transitionRepository = keyguardTransitionRepository,
transitionInteractor = keyguardTransitionInteractor,
+ internalTransitionInteractor = internalKeyguardTransitionInteractor,
scope = applicationCoroutineScope,
bgDispatcher = testDispatcher,
mainDispatcher = testDispatcher,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt
index 135644c..a9be06d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt
@@ -28,6 +28,7 @@
FromDreamingTransitionInteractor(
transitionRepository = keyguardTransitionRepository,
transitionInteractor = keyguardTransitionInteractor,
+ internalTransitionInteractor = internalKeyguardTransitionInteractor,
scope = applicationCoroutineScope,
bgDispatcher = testDispatcher,
mainDispatcher = testDispatcher,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractorKosmos.kt
index 1695327..6784658 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractorKosmos.kt
@@ -28,6 +28,7 @@
FromGlanceableHubTransitionInteractor(
transitionRepository = keyguardTransitionRepository,
transitionInteractor = keyguardTransitionInteractor,
+ internalTransitionInteractor = internalKeyguardTransitionInteractor,
scope = applicationCoroutineScope,
bgDispatcher = testDispatcher,
mainDispatcher = testDispatcher,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt
index 4039ee6..317294f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt
@@ -31,6 +31,7 @@
FromGoneTransitionInteractor(
transitionRepository = fakeKeyguardTransitionRepository,
transitionInteractor = keyguardTransitionInteractor,
+ internalTransitionInteractor = internalKeyguardTransitionInteractor,
scope = applicationCoroutineScope,
bgDispatcher = testDispatcher,
mainDispatcher = testDispatcher,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
index 28bd439..4131145 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
@@ -29,6 +29,7 @@
FromLockscreenTransitionInteractor(
transitionRepository = keyguardTransitionRepository,
transitionInteractor = keyguardTransitionInteractor,
+ internalTransitionInteractor = internalKeyguardTransitionInteractor,
scope = applicationCoroutineScope,
bgDispatcher = testDispatcher,
mainDispatcher = testDispatcher,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorKosmos.kt
index fc740a1..c216945 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorKosmos.kt
@@ -29,6 +29,7 @@
FromOccludedTransitionInteractor(
transitionRepository = keyguardTransitionRepository,
transitionInteractor = keyguardTransitionInteractor,
+ internalTransitionInteractor = internalKeyguardTransitionInteractor,
scope = applicationCoroutineScope,
bgDispatcher = testDispatcher,
mainDispatcher = testDispatcher,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt
index d72b9c1..42ee152 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt
@@ -31,6 +31,7 @@
FromPrimaryBouncerTransitionInteractor(
transitionRepository = keyguardTransitionRepository,
transitionInteractor = keyguardTransitionInteractor,
+ internalTransitionInteractor = internalKeyguardTransitionInteractor,
scope = applicationCoroutineScope,
bgDispatcher = testDispatcher,
mainDispatcher = testDispatcher,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractorKosmos.kt
new file mode 100644
index 0000000..017a9ec
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractorKosmos.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.internalKeyguardTransitionInteractor by
+ Kosmos.Fixture {
+ InternalKeyguardTransitionInteractor(
+ repository = keyguardTransitionRepository,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt
index 0667a6b..c6b5ed0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt
@@ -28,5 +28,6 @@
keyguardRepository,
biometricSettingsRepository,
keyguardTransitionInteractor,
+ internalTransitionInteractor = internalKeyguardTransitionInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractorKosmos.kt
index 7d8d33f..5836902 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractorKosmos.kt
@@ -30,5 +30,6 @@
deviceProvisioningInteractor = deviceProvisioningInteractor,
keyguardTransitionInteractor = keyguardTransitionInteractor,
repository = keyguardTransitionRepository,
+ internalTransitionInteractor = internalKeyguardTransitionInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
index c90642d..c5da10e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
@@ -33,6 +33,6 @@
fromAodTransitionInteractor = { fromAodTransitionInteractor },
fromAlternateBouncerTransitionInteractor = { fromAlternateBouncerTransitionInteractor },
fromDozingTransitionInteractor = { fromDozingTransitionInteractor },
- sceneInteractor = { sceneInteractor }
+ sceneInteractor = sceneInteractor
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
index 3c1f7b1..e50e044 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.domain.interactor.scenetransition
import com.android.systemui.keyguard.data.repository.lockscreenSceneTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.internalKeyguardTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
@@ -29,5 +30,6 @@
applicationScope = applicationCoroutineScope,
sceneInteractor = sceneInteractor,
repository = lockscreenSceneTransitionRepository,
+ internalTransitionInteractor = internalKeyguardTransitionInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt
index 0921eb9..ae8b411 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt
@@ -31,7 +31,7 @@
repository = sceneContainerRepository,
logger = sceneLogger,
sceneFamilyResolvers = { sceneFamilyResolvers },
- deviceUnlockedInteractor = deviceUnlockedInteractor,
- keyguardEnabledInteractor = keyguardEnabledInteractor,
+ deviceUnlockedInteractor = { deviceUnlockedInteractor },
+ keyguardEnabledInteractor = { keyguardEnabledInteractor },
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardOcclusionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardOcclusionInteractorKosmos.kt
index a90a9ff..65016c3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardOcclusionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardOcclusionInteractorKosmos.kt
@@ -19,6 +19,7 @@
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardOcclusionInteractor
+import com.android.systemui.keyguard.domain.interactor.internalKeyguardTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.kosmos.Kosmos
@@ -34,5 +35,6 @@
transitionInteractor = keyguardTransitionInteractor,
keyguardInteractor = keyguardInteractor,
deviceUnlockedInteractor = { deviceUnlockedInteractor },
+ internalTransitionInteractor = internalKeyguardTransitionInteractor,
)
}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodBaseContext.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodBaseContext.java
index 63bcda188..4992c4b 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodBaseContext.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodBaseContext.java
@@ -447,14 +447,6 @@
}
@Override
- public void sendOrderedBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user,
- String[] receiverPermissions, int appOp, Bundle options,
- BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
- String initialData, Bundle initialExtras) {
- throw notSupported();
- }
-
- @Override
public void sendStickyBroadcast(Intent intent) {
throw notSupported();
}
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index c4d38e4..a2bbff0 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -48,3 +48,6 @@
# SystemConfig
per-file SystemConfig.java = file:/PACKAGE_MANAGER_OWNERS
+
+# CertBlocklister
+per-file Cert*.java = tweek@google.com, brambonne@google.com, prb@google.com, miguelaranda@google.com
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index dfd148d..a4026eb 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -241,12 +241,14 @@
* opportunity to reset any settings depending on our rescue level.
*/
public static void onSettingsProviderPublished(Context context) {
- handleNativeRescuePartyResets();
- ContentResolver contentResolver = context.getContentResolver();
- DeviceConfig.setMonitorCallback(
- contentResolver,
- Executors.newSingleThreadExecutor(),
- new RescuePartyMonitorCallback(context));
+ if (!Flags.deprecateFlagsAndSettingsResets()) {
+ handleNativeRescuePartyResets();
+ ContentResolver contentResolver = context.getContentResolver();
+ DeviceConfig.setMonitorCallback(
+ contentResolver,
+ Executors.newSingleThreadExecutor(),
+ new RescuePartyMonitorCallback(context));
+ }
}
@@ -256,75 +258,81 @@
* on modules of newer versions.
*/
public static void resetDeviceConfigForPackages(List<String> packageNames) {
- if (packageNames == null) {
- return;
- }
- Set<String> namespacesToReset = new ArraySet<String>();
- Iterator<String> it = packageNames.iterator();
- RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstanceIfCreated();
- // Get runtime package to namespace mapping if created.
- if (rescuePartyObserver != null) {
- while (it.hasNext()) {
- String packageName = it.next();
- Set<String> runtimeAffectedNamespaces =
- rescuePartyObserver.getAffectedNamespaceSet(packageName);
- if (runtimeAffectedNamespaces != null) {
- namespacesToReset.addAll(runtimeAffectedNamespaces);
+ if (!Flags.deprecateFlagsAndSettingsResets()) {
+ if (packageNames == null) {
+ return;
+ }
+ Set<String> namespacesToReset = new ArraySet<String>();
+ Iterator<String> it = packageNames.iterator();
+ RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstanceIfCreated();
+ // Get runtime package to namespace mapping if created.
+ if (rescuePartyObserver != null) {
+ while (it.hasNext()) {
+ String packageName = it.next();
+ Set<String> runtimeAffectedNamespaces =
+ rescuePartyObserver.getAffectedNamespaceSet(packageName);
+ if (runtimeAffectedNamespaces != null) {
+ namespacesToReset.addAll(runtimeAffectedNamespaces);
+ }
}
}
- }
- // Get preset package to namespace mapping if created.
- Set<String> presetAffectedNamespaces = getPresetNamespacesForPackages(
- packageNames);
- if (presetAffectedNamespaces != null) {
- namespacesToReset.addAll(presetAffectedNamespaces);
- }
+ // Get preset package to namespace mapping if created.
+ Set<String> presetAffectedNamespaces = getPresetNamespacesForPackages(
+ packageNames);
+ if (presetAffectedNamespaces != null) {
+ namespacesToReset.addAll(presetAffectedNamespaces);
+ }
- // Clear flags under the namespaces mapped to these packages.
- // Using setProperties since DeviceConfig.resetToDefaults bans the current flag set.
- Iterator<String> namespaceIt = namespacesToReset.iterator();
- while (namespaceIt.hasNext()) {
- String namespaceToReset = namespaceIt.next();
- Properties properties = new Properties.Builder(namespaceToReset).build();
- try {
- if (!DeviceConfig.setProperties(properties)) {
- logCriticalInfo(Log.ERROR, "Failed to clear properties under "
+ // Clear flags under the namespaces mapped to these packages.
+ // Using setProperties since DeviceConfig.resetToDefaults bans the current flag set.
+ Iterator<String> namespaceIt = namespacesToReset.iterator();
+ while (namespaceIt.hasNext()) {
+ String namespaceToReset = namespaceIt.next();
+ Properties properties = new Properties.Builder(namespaceToReset).build();
+ try {
+ if (!DeviceConfig.setProperties(properties)) {
+ logCriticalInfo(Log.ERROR, "Failed to clear properties under "
+ namespaceToReset
+ ". Running `device_config get_sync_disabled_for_tests` will confirm"
+ " if config-bulk-update is enabled.");
+ }
+ } catch (DeviceConfig.BadConfigException exception) {
+ logCriticalInfo(Log.WARN, "namespace " + namespaceToReset
+ + " is already banned, skip reset.");
}
- } catch (DeviceConfig.BadConfigException exception) {
- logCriticalInfo(Log.WARN, "namespace " + namespaceToReset
- + " is already banned, skip reset.");
}
}
}
private static Set<String> getPresetNamespacesForPackages(List<String> packageNames) {
Set<String> resultSet = new ArraySet<String>();
- try {
- String flagVal = DeviceConfig.getString(NAMESPACE_CONFIGURATION,
- NAMESPACE_TO_PACKAGE_MAPPING_FLAG, "");
- String[] mappingEntries = flagVal.split(",");
- for (int i = 0; i < mappingEntries.length; i++) {
- if (TextUtils.isEmpty(mappingEntries[i])) {
- continue;
- }
- String[] splittedEntry = mappingEntries[i].split(":");
- if (splittedEntry.length != 2) {
- throw new RuntimeException("Invalid mapping entry: " + mappingEntries[i]);
- }
- String namespace = splittedEntry[0];
- String packageName = splittedEntry[1];
+ if (!Flags.deprecateFlagsAndSettingsResets()) {
+ try {
+ String flagVal = DeviceConfig.getString(NAMESPACE_CONFIGURATION,
+ NAMESPACE_TO_PACKAGE_MAPPING_FLAG, "");
+ String[] mappingEntries = flagVal.split(",");
+ for (int i = 0; i < mappingEntries.length; i++) {
+ if (TextUtils.isEmpty(mappingEntries[i])) {
+ continue;
+ }
+ String[] splitEntry = mappingEntries[i].split(":");
+ if (splitEntry.length != 2) {
+ throw new RuntimeException("Invalid mapping entry: " + mappingEntries[i]);
+ }
+ String namespace = splitEntry[0];
+ String packageName = splitEntry[1];
- if (packageNames.contains(packageName)) {
- resultSet.add(namespace);
+ if (packageNames.contains(packageName)) {
+ resultSet.add(namespace);
+ }
}
+ } catch (Exception e) {
+ resultSet.clear();
+ Slog.e(TAG, "Failed to read preset package to namespaces mapping.", e);
+ } finally {
+ return resultSet;
}
- } catch (Exception e) {
- resultSet.clear();
- Slog.e(TAG, "Failed to read preset package to namespaces mapping.", e);
- } finally {
+ } else {
return resultSet;
}
}
@@ -342,43 +350,54 @@
}
public void onNamespaceUpdate(@NonNull String updatedNamespace) {
- startObservingPackages(mContext, updatedNamespace);
+ if (!Flags.deprecateFlagsAndSettingsResets()) {
+ startObservingPackages(mContext, updatedNamespace);
+ }
}
public void onDeviceConfigAccess(@NonNull String callingPackage,
@NonNull String namespace) {
- RescuePartyObserver.getInstance(mContext).recordDeviceConfigAccess(
- callingPackage,
- namespace);
+
+ if (!Flags.deprecateFlagsAndSettingsResets()) {
+ RescuePartyObserver.getInstance(mContext).recordDeviceConfigAccess(
+ callingPackage,
+ namespace);
+ }
}
}
private static void startObservingPackages(Context context, @NonNull String updatedNamespace) {
- RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstance(context);
- Set<String> callingPackages = rescuePartyObserver.getCallingPackagesSet(updatedNamespace);
- if (callingPackages == null) {
- return;
+ if (!Flags.deprecateFlagsAndSettingsResets()) {
+ RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstance(context);
+ Set<String> callingPackages = rescuePartyObserver.getCallingPackagesSet(
+ updatedNamespace);
+ if (callingPackages == null) {
+ return;
+ }
+ List<String> callingPackageList = new ArrayList<>();
+ callingPackageList.addAll(callingPackages);
+ Slog.i(TAG, "Starting to observe: " + callingPackageList + ", updated namespace: "
+ + updatedNamespace);
+ PackageWatchdog.getInstance(context).startObservingHealth(
+ rescuePartyObserver,
+ callingPackageList,
+ DEFAULT_OBSERVING_DURATION_MS);
}
- List<String> callingPackageList = new ArrayList<>();
- callingPackageList.addAll(callingPackages);
- Slog.i(TAG, "Starting to observe: " + callingPackageList + ", updated namespace: "
- + updatedNamespace);
- PackageWatchdog.getInstance(context).startObservingHealth(
- rescuePartyObserver,
- callingPackageList,
- DEFAULT_OBSERVING_DURATION_MS);
}
private static void handleNativeRescuePartyResets() {
- if (SettingsToPropertiesMapper.isNativeFlagsResetPerformed()) {
- String[] resetNativeCategories = SettingsToPropertiesMapper.getResetNativeCategories();
- for (int i = 0; i < resetNativeCategories.length; i++) {
- // Don't let RescueParty reset the namespace for RescueParty switches.
- if (NAMESPACE_CONFIGURATION.equals(resetNativeCategories[i])) {
- continue;
+ if (!Flags.deprecateFlagsAndSettingsResets()) {
+ if (SettingsToPropertiesMapper.isNativeFlagsResetPerformed()) {
+ String[] resetNativeCategories =
+ SettingsToPropertiesMapper.getResetNativeCategories();
+ for (int i = 0; i < resetNativeCategories.length; i++) {
+ // Don't let RescueParty reset the namespace for RescueParty switches.
+ if (NAMESPACE_CONFIGURATION.equals(resetNativeCategories[i])) {
+ continue;
+ }
+ DeviceConfig.resetToDefaults(DEVICE_CONFIG_RESET_MODE,
+ resetNativeCategories[i]);
}
- DeviceConfig.resetToDefaults(DEVICE_CONFIG_RESET_MODE,
- resetNativeCategories[i]);
}
}
}
@@ -400,6 +419,13 @@
}
}
+ private static int getMaxRescueLevel() {
+ if (!SystemProperties.getBoolean(PROP_DISABLE_FACTORY_RESET_FLAG, false)) {
+ return Level.factoryReset();
+ }
+ return Level.reboot();
+ }
+
/**
* Get the rescue level to perform if this is the n-th attempt at mitigating failure.
*
@@ -409,19 +435,30 @@
* @return the rescue level for the n-th mitigation attempt.
*/
private static int getRescueLevel(int mitigationCount, boolean mayPerformReboot) {
- if (mitigationCount == 1) {
- return LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS;
- } else if (mitigationCount == 2) {
- return LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES;
- } else if (mitigationCount == 3) {
- return LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS;
- } else if (mitigationCount == 4) {
- return Math.min(getMaxRescueLevel(mayPerformReboot), LEVEL_WARM_REBOOT);
- } else if (mitigationCount >= 5) {
- return Math.min(getMaxRescueLevel(mayPerformReboot), LEVEL_FACTORY_RESET);
+ if (!Flags.deprecateFlagsAndSettingsResets()) {
+ if (mitigationCount == 1) {
+ return LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS;
+ } else if (mitigationCount == 2) {
+ return LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES;
+ } else if (mitigationCount == 3) {
+ return LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS;
+ } else if (mitigationCount == 4) {
+ return Math.min(getMaxRescueLevel(mayPerformReboot), LEVEL_WARM_REBOOT);
+ } else if (mitigationCount >= 5) {
+ return Math.min(getMaxRescueLevel(mayPerformReboot), LEVEL_FACTORY_RESET);
+ } else {
+ Slog.w(TAG, "Expected positive mitigation count, was " + mitigationCount);
+ return LEVEL_NONE;
+ }
} else {
- Slog.w(TAG, "Expected positive mitigation count, was " + mitigationCount);
- return LEVEL_NONE;
+ if (mitigationCount == 1) {
+ return Level.reboot();
+ } else if (mitigationCount >= 2) {
+ return Math.min(getMaxRescueLevel(), Level.factoryReset());
+ } else {
+ Slog.w(TAG, "Expected positive mitigation count, was " + mitigationCount);
+ return LEVEL_NONE;
+ }
}
}
@@ -451,13 +488,13 @@
return Math.min(getMaxRescueLevel(mayPerformReboot), RESCUE_LEVEL_WARM_REBOOT);
} else if (mitigationCount == 4) {
return Math.min(getMaxRescueLevel(mayPerformReboot),
- RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS);
+ RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS);
} else if (mitigationCount == 5) {
return Math.min(getMaxRescueLevel(mayPerformReboot),
- RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES);
+ RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES);
} else if (mitigationCount == 6) {
return Math.min(getMaxRescueLevel(mayPerformReboot),
- RESCUE_LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS);
+ RESCUE_LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS);
} else if (mitigationCount >= 7) {
return Math.min(getMaxRescueLevel(mayPerformReboot), RESCUE_LEVEL_FACTORY_RESET);
} else {
@@ -465,6 +502,22 @@
}
}
+ /**
+ * Get the rescue level to perform if this is the n-th attempt at mitigating failure.
+ *
+ * @param mitigationCount the mitigation attempt number (1 = first attempt etc.).
+ * @return the rescue level for the n-th mitigation attempt.
+ */
+ private static @RescueLevels int getRescueLevel(int mitigationCount) {
+ if (mitigationCount == 1) {
+ return Level.reboot();
+ } else if (mitigationCount >= 2) {
+ return Math.min(getMaxRescueLevel(), Level.factoryReset());
+ } else {
+ return Level.none();
+ }
+ }
+
private static void executeRescueLevel(Context context, @Nullable String failedPackage,
int level) {
Slog.w(TAG, "Attempting rescue level " + levelToString(level));
@@ -537,13 +590,22 @@
executeWarmReboot(context, level, failedPackage);
break;
case RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS:
- resetAllSettingsIfNecessary(context, Settings.RESET_MODE_UNTRUSTED_DEFAULTS, level);
+ if (!Flags.deprecateFlagsAndSettingsResets()) {
+ resetAllSettingsIfNecessary(context, Settings.RESET_MODE_UNTRUSTED_DEFAULTS,
+ level);
+ }
break;
case RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES:
- resetAllSettingsIfNecessary(context, Settings.RESET_MODE_UNTRUSTED_CHANGES, level);
+ if (!Flags.deprecateFlagsAndSettingsResets()) {
+ resetAllSettingsIfNecessary(context, Settings.RESET_MODE_UNTRUSTED_CHANGES,
+ level);
+ }
break;
case RESCUE_LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS:
- resetAllSettingsIfNecessary(context, Settings.RESET_MODE_TRUSTED_DEFAULTS, level);
+ if (!Flags.deprecateFlagsAndSettingsResets()) {
+ resetAllSettingsIfNecessary(context, Settings.RESET_MODE_TRUSTED_DEFAULTS,
+ level);
+ }
break;
case RESCUE_LEVEL_FACTORY_RESET:
// Before the completion of Reboot, if any crash happens then PackageWatchdog
@@ -560,6 +622,12 @@
private static void executeWarmReboot(Context context, int level,
@Nullable String failedPackage) {
+ if (Flags.deprecateFlagsAndSettingsResets()) {
+ if (shouldThrottleReboot()) {
+ return;
+ }
+ }
+
// Request the reboot from a separate thread to avoid deadlock on PackageWatchdog
// when device shutting down.
setRebootProperty(true);
@@ -579,6 +647,11 @@
private static void executeFactoryReset(Context context, int level,
@Nullable String failedPackage) {
+ if (Flags.deprecateFlagsAndSettingsResets()) {
+ if (shouldThrottleReboot()) {
+ return;
+ }
+ }
setFactoryResetProperty(true);
long now = System.currentTimeMillis();
setLastFactoryResetTimeMs(now);
@@ -655,30 +728,32 @@
private static void resetAllSettingsIfNecessary(Context context, int mode,
int level) throws Exception {
- // No need to reset Settings again if they are already reset in the current level once.
- if (getMaxRescueLevelAttempted() >= level) {
- return;
- }
- setMaxRescueLevelAttempted(level);
- // Try our best to reset all settings possible, and once finished
- // rethrow any exception that we encountered
- Exception res = null;
- final ContentResolver resolver = context.getContentResolver();
- try {
- Settings.Global.resetToDefaultsAsUser(resolver, null, mode,
- UserHandle.SYSTEM.getIdentifier());
- } catch (Exception e) {
- res = new RuntimeException("Failed to reset global settings", e);
- }
- for (int userId : getAllUserIds()) {
- try {
- Settings.Secure.resetToDefaultsAsUser(resolver, null, mode, userId);
- } catch (Exception e) {
- res = new RuntimeException("Failed to reset secure settings for " + userId, e);
+ if (!Flags.deprecateFlagsAndSettingsResets()) {
+ // No need to reset Settings again if they are already reset in the current level once.
+ if (getMaxRescueLevelAttempted() >= level) {
+ return;
}
- }
- if (res != null) {
- throw res;
+ setMaxRescueLevelAttempted(level);
+ // Try our best to reset all settings possible, and once finished
+ // rethrow any exception that we encountered
+ Exception res = null;
+ final ContentResolver resolver = context.getContentResolver();
+ try {
+ Settings.Global.resetToDefaultsAsUser(resolver, null, mode,
+ UserHandle.SYSTEM.getIdentifier());
+ } catch (Exception e) {
+ res = new RuntimeException("Failed to reset global settings", e);
+ }
+ for (int userId : getAllUserIds()) {
+ try {
+ Settings.Secure.resetToDefaultsAsUser(resolver, null, mode, userId);
+ } catch (Exception e) {
+ res = new RuntimeException("Failed to reset secure settings for " + userId, e);
+ }
+ }
+ if (res != null) {
+ throw res;
+ }
}
}
@@ -731,11 +806,15 @@
if (!isDisabled() && (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH
|| failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING)) {
if (Flags.recoverabilityDetection()) {
- return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount,
- mayPerformReboot(failedPackage), failedPackage));
+ if (!Flags.deprecateFlagsAndSettingsResets()) {
+ return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount,
+ mayPerformReboot(failedPackage), failedPackage));
+ } else {
+ return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount));
+ }
} else {
return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount,
- mayPerformReboot(failedPackage)));
+ mayPerformReboot(failedPackage)));
}
} else {
return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
@@ -750,10 +829,17 @@
}
if (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH
|| failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING) {
- final int level = Flags.recoverabilityDetection() ? getRescueLevel(mitigationCount,
- mayPerformReboot(failedPackage), failedPackage)
- : getRescueLevel(mitigationCount,
- mayPerformReboot(failedPackage));
+ final int level;
+ if (Flags.recoverabilityDetection()) {
+ if (!Flags.deprecateFlagsAndSettingsResets()) {
+ level = getRescueLevel(mitigationCount, mayPerformReboot(failedPackage),
+ failedPackage);
+ } else {
+ level = getRescueLevel(mitigationCount);
+ }
+ } else {
+ level = getRescueLevel(mitigationCount, mayPerformReboot(failedPackage));
+ }
executeRescueLevel(mContext,
failedPackage == null ? null : failedPackage.getPackageName(), level);
return true;
@@ -787,8 +873,12 @@
return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
}
if (Flags.recoverabilityDetection()) {
- return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount,
- true, /*failedPackage=*/ null));
+ if (!Flags.deprecateFlagsAndSettingsResets()) {
+ return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount,
+ true, /*failedPackage=*/ null));
+ } else {
+ return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount));
+ }
} else {
return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, true));
}
@@ -800,9 +890,17 @@
return false;
}
boolean mayPerformReboot = !shouldThrottleReboot();
- final int level = Flags.recoverabilityDetection() ? getRescueLevel(mitigationCount,
- mayPerformReboot, /*failedPackage=*/ null)
- : getRescueLevel(mitigationCount, mayPerformReboot);
+ final int level;
+ if (Flags.recoverabilityDetection()) {
+ if (!Flags.deprecateFlagsAndSettingsResets()) {
+ level = getRescueLevel(mitigationCount, mayPerformReboot,
+ /*failedPackage=*/ null);
+ } else {
+ level = getRescueLevel(mitigationCount);
+ }
+ } else {
+ level = getRescueLevel(mitigationCount, mayPerformReboot);
+ }
executeRescueLevel(mContext, /*failedPackage=*/ null, level);
return true;
}
@@ -828,18 +926,6 @@
return isPersistentSystemApp(failingPackage.getPackageName());
}
- /**
- * Returns {@code true} if Rescue Party is allowed to attempt a reboot or factory reset.
- * Will return {@code false} if a factory reset was already offered recently.
- */
- private boolean shouldThrottleReboot() {
- Long lastResetTime = getLastFactoryResetTimeMs();
- long now = System.currentTimeMillis();
- long throttleDurationMin = SystemProperties.getLong(PROP_THROTTLE_DURATION_MIN_FLAG,
- DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN);
- return now < lastResetTime + TimeUnit.MINUTES.toMillis(throttleDurationMin);
- }
-
private boolean isPersistentSystemApp(@NonNull String packageName) {
PackageManager pm = mContext.getPackageManager();
try {
@@ -852,20 +938,22 @@
private synchronized void recordDeviceConfigAccess(@NonNull String callingPackage,
@NonNull String namespace) {
- // Record it in calling packages to namespace map
- Set<String> namespaceSet = mCallingPackageNamespaceSetMap.get(callingPackage);
- if (namespaceSet == null) {
- namespaceSet = new ArraySet<>();
- mCallingPackageNamespaceSetMap.put(callingPackage, namespaceSet);
+ if (!Flags.deprecateFlagsAndSettingsResets()) {
+ // Record it in calling packages to namespace map
+ Set<String> namespaceSet = mCallingPackageNamespaceSetMap.get(callingPackage);
+ if (namespaceSet == null) {
+ namespaceSet = new ArraySet<>();
+ mCallingPackageNamespaceSetMap.put(callingPackage, namespaceSet);
+ }
+ namespaceSet.add(namespace);
+ // Record it in namespace to calling packages map
+ Set<String> callingPackageSet = mNamespaceCallingPackageSetMap.get(namespace);
+ if (callingPackageSet == null) {
+ callingPackageSet = new ArraySet<>();
+ }
+ callingPackageSet.add(callingPackage);
+ mNamespaceCallingPackageSetMap.put(namespace, callingPackageSet);
}
- namespaceSet.add(namespace);
- // Record it in namespace to calling packages map
- Set<String> callingPackageSet = mNamespaceCallingPackageSetMap.get(namespace);
- if (callingPackageSet == null) {
- callingPackageSet = new ArraySet<>();
- }
- callingPackageSet.add(callingPackage);
- mNamespaceCallingPackageSetMap.put(namespace, callingPackageSet);
}
private synchronized Set<String> getAffectedNamespaceSet(String failedPackage) {
@@ -881,6 +969,18 @@
}
}
+ /**
+ * Returns {@code true} if Rescue Party is allowed to attempt a reboot or factory reset.
+ * Will return {@code false} if a factory reset was already offered recently.
+ */
+ private static boolean shouldThrottleReboot() {
+ Long lastResetTime = getLastFactoryResetTimeMs();
+ long now = System.currentTimeMillis();
+ long throttleDurationMin = SystemProperties.getLong(PROP_THROTTLE_DURATION_MIN_FLAG,
+ DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN);
+ return now < lastResetTime + TimeUnit.MINUTES.toMillis(throttleDurationMin);
+ }
+
private static int[] getAllUserIds() {
int systemUserId = UserHandle.SYSTEM.getIdentifier();
int[] userIds = { systemUserId };
@@ -919,6 +1019,22 @@
}
}
+ private static class Level {
+ static int none() {
+ return Flags.recoverabilityDetection() ? RESCUE_LEVEL_NONE : LEVEL_NONE;
+ }
+
+ static int reboot() {
+ return Flags.recoverabilityDetection() ? RESCUE_LEVEL_WARM_REBOOT : LEVEL_WARM_REBOOT;
+ }
+
+ static int factoryReset() {
+ return Flags.recoverabilityDetection()
+ ? RESCUE_LEVEL_FACTORY_RESET
+ : LEVEL_FACTORY_RESET;
+ }
+ }
+
private static String levelToString(int level) {
if (Flags.recoverabilityDetection()) {
switch (level) {
diff --git a/services/core/java/com/android/server/am/LmkdConnection.java b/services/core/java/com/android/server/am/LmkdConnection.java
index 598f086..4faadcb 100644
--- a/services/core/java/com/android/server/am/LmkdConnection.java
+++ b/services/core/java/com/android/server/am/LmkdConnection.java
@@ -91,10 +91,18 @@
@GuardedBy("mLmkdSocketLock")
private LocalSocket mLmkdSocket = null;
- // socket I/O streams
- @GuardedBy("mLmkdSocketLock")
+ // mutex to synchronize socket output stream with socket creation/destruction
+ private final Object mLmkdOutputStreamLock = new Object();
+
+ // socket output stream
+ @GuardedBy("mLmkdOutputStreamLock")
private OutputStream mLmkdOutputStream = null;
- @GuardedBy("mLmkdSocketLock")
+
+ // mutex to synchronize socket input stream with socket creation/destruction
+ private final Object mLmkdInputStreamLock = new Object();
+
+ // socket input stream
+ @GuardedBy("mLmkdInputStreamLock")
private InputStream mLmkdInputStream = null;
// buffer to store incoming data
@@ -148,9 +156,13 @@
return false;
}
// connection established
- mLmkdSocket = socket;
- mLmkdOutputStream = ostream;
- mLmkdInputStream = istream;
+ synchronized(mLmkdOutputStreamLock) {
+ synchronized(mLmkdInputStreamLock) {
+ mLmkdSocket = socket;
+ mLmkdOutputStream = ostream;
+ mLmkdInputStream = istream;
+ }
+ }
mMsgQueue.addOnFileDescriptorEventListener(mLmkdSocket.getFileDescriptor(),
EVENT_INPUT | EVENT_ERROR,
new MessageQueue.OnFileDescriptorEventListener() {
@@ -177,7 +189,13 @@
mMsgQueue.removeOnFileDescriptorEventListener(
mLmkdSocket.getFileDescriptor());
IoUtils.closeQuietly(mLmkdSocket);
- mLmkdSocket = null;
+ synchronized(mLmkdOutputStreamLock) {
+ synchronized(mLmkdInputStreamLock) {
+ mLmkdOutputStream = null;
+ mLmkdInputStream = null;
+ mLmkdSocket = null;
+ }
+ }
}
// wake up reply waiters if any
synchronized (mReplyBufLock) {
@@ -262,24 +280,33 @@
}
private boolean write(ByteBuffer buf) {
- synchronized (mLmkdSocketLock) {
- try {
- mLmkdOutputStream.write(buf.array(), 0, buf.position());
- } catch (IOException ex) {
- return false;
+ boolean result = false;
+
+ synchronized(mLmkdOutputStreamLock) {
+ if (mLmkdOutputStream != null) {
+ try {
+ mLmkdOutputStream.write(buf.array(), 0, buf.position());
+ result = true;
+ } catch (IOException ex) {
+ }
}
- return true;
}
+
+ return result;
}
private int read(ByteBuffer buf) {
- synchronized (mLmkdSocketLock) {
- try {
- return mLmkdInputStream.read(buf.array(), 0, buf.array().length);
- } catch (IOException ex) {
+ int result = -1;
+
+ synchronized(mLmkdInputStreamLock) {
+ if (mLmkdInputStream != null) {
+ try {
+ result = mLmkdInputStream.read(buf.array(), 0, buf.array().length);
+ } catch (IOException ex) {
+ }
}
- return -1;
}
+ return result;
}
/**
diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
index d061e2d..fbd32a6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
@@ -31,7 +31,6 @@
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
-import com.android.server.biometrics.sensors.fingerprint.aidl.AidlSession;
import java.util.function.Supplier;
@@ -203,16 +202,6 @@
}
}
- // TODO(b/317414324): Deprecate setIgnoreDisplayTouches
- protected final void resetIgnoreDisplayTouches() {
- final AidlSession session = (AidlSession) getFreshDaemon();
- try {
- session.getSession().setIgnoreDisplayTouches(false);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when resetting setIgnoreDisplayTouches");
- }
- }
-
@Override
public boolean isInterruptable() {
return true;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 4c86f57..60cfd5a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -962,6 +962,19 @@
provider.onUdfpsUiEvent(event, requestId, sensorId);
}
+ @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
+ @Override
+ public void setIgnoreDisplayTouches(long requestId, int sensorId, boolean ignoreTouches) {
+ super.setIgnoreDisplayTouches_enforcePermission();
+
+ final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId);
+ if (provider == null) {
+ Slog.w(TAG,
+ "No matching provider for setIgnoreDisplayTouches, sensorId: " + sensorId);
+ return;
+ }
+ provider.setIgnoreDisplayTouches(requestId, sensorId, ignoreTouches);
+ }
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index a6cf2f4..e4a99e6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -134,6 +134,8 @@
void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller);
+ void setIgnoreDisplayTouches(long requestId, int sensorId, boolean ignoreTouches);
+
void onPowerPressed();
@NonNull
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java
index dce0175..15d7a47 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java
@@ -31,4 +31,5 @@
void onPointerUp(PointerContext pc);
void onUdfpsUiEvent(@FingerprintManager.UdfpsUiEvent int event);
boolean isPointerDown();
+ void setIgnoreDisplayTouches(boolean ignoreTouches);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 72d92b9..d04afdb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -33,7 +33,6 @@
import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.biometrics.common.ICancellationSignal;
-import android.hardware.biometrics.common.OperationState;
import android.hardware.biometrics.events.AuthenticationAcquiredInfo;
import android.hardware.biometrics.events.AuthenticationErrorInfo;
import android.hardware.biometrics.events.AuthenticationFailedInfo;
@@ -182,7 +181,6 @@
handleLockout(authenticated);
if (authenticated) {
mState = STATE_STOPPED;
- resetIgnoreDisplayTouches();
mSensorOverlays.hide(getSensorId());
if (reportBiometricAuthAttempts()) {
mAuthenticationStateListeners.onAuthenticationSucceeded(
@@ -223,7 +221,6 @@
// Send the error, but do not invoke the FinishCallback yet. Since lockout is not
// controlled by the HAL, the framework must stop the sensor before finishing the
// client.
- resetIgnoreDisplayTouches();
mSensorOverlays.hide(getSensorId());
mAuthenticationStateListeners.onAuthenticationError(
new AuthenticationErrorInfo.Builder(BiometricSourceType.FINGERPRINT,
@@ -275,7 +272,6 @@
BiometricNotificationUtils.showBadCalibrationNotification(getContext());
}
- resetIgnoreDisplayTouches();
mSensorOverlays.hide(getSensorId());
mAuthenticationStateListeners.onAuthenticationStopped(new AuthenticationStoppedInfo
.Builder(BiometricSourceType.FINGERPRINT, getRequestReason()).build()
@@ -284,7 +280,6 @@
@Override
protected void startHalOperation() {
- resetIgnoreDisplayTouches();
mSensorOverlays.show(getSensorId(), getRequestReason(), this);
mAuthenticationStateListeners.onAuthenticationStarted(new AuthenticationStartedInfo
.Builder(BiometricSourceType.FINGERPRINT, getRequestReason()).build()
@@ -331,12 +326,6 @@
if (session.hasContextMethods()) {
try {
session.getSession().onContextChanged(ctx);
- // TODO(b/317414324): Deprecate setIgnoreDisplayTouches
- if (ctx.operationState != null && ctx.operationState.getTag()
- == OperationState.fingerprintOperationState) {
- session.getSession().setIgnoreDisplayTouches(ctx.operationState
- .getFingerprintOperationState().isHardwareIgnoringTouches);
- }
} catch (RemoteException e) {
Slog.e(TAG, "Unable to notify context changed", e);
}
@@ -353,7 +342,6 @@
@Override
protected void stopHalOperation() {
- resetIgnoreDisplayTouches();
mSensorOverlays.hide(getSensorId());
mAuthenticationStateListeners.onAuthenticationStopped(new AuthenticationStoppedInfo
.Builder(BiometricSourceType.FINGERPRINT, getRequestReason()).build()
@@ -415,6 +403,15 @@
}
@Override
+ public void setIgnoreDisplayTouches(boolean ignoreTouches) {
+ try {
+ getFreshDaemon().getSession().setIgnoreDisplayTouches(ignoreTouches);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+
+ @Override
public boolean isPointerDown() {
return mIsPointerDown;
}
@@ -457,7 +454,6 @@
Slog.e(TAG, "Remote exception", e);
}
- resetIgnoreDisplayTouches();
mSensorOverlays.hide(getSensorId());
mAuthenticationStateListeners.onAuthenticationStopped(new AuthenticationStoppedInfo
.Builder(BiometricSourceType.FINGERPRINT, getRequestReason()).build()
@@ -492,7 +488,6 @@
Slog.e(TAG, "Remote exception", e);
}
- resetIgnoreDisplayTouches();
mSensorOverlays.hide(getSensorId());
mAuthenticationStateListeners.onAuthenticationStopped(new AuthenticationStoppedInfo
.Builder(BiometricSourceType.FINGERPRINT, getRequestReason()).build()
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
index 36af5db..fb48053 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -87,7 +87,6 @@
@Override
protected void stopHalOperation() {
- resetIgnoreDisplayTouches();
mSensorOverlays.hide(getSensorId());
mAuthenticationStateListeners.onAuthenticationStopped(
new AuthenticationStoppedInfo.Builder(BiometricSourceType.FINGERPRINT,
@@ -107,7 +106,6 @@
@Override
protected void startHalOperation() {
- resetIgnoreDisplayTouches();
mSensorOverlays.show(getSensorId(), BiometricRequestConstants.REASON_AUTH_KEYGUARD,
this);
mAuthenticationStateListeners.onAuthenticationStarted(
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index 3a72d7e..993a68f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -150,7 +150,6 @@
controller -> controller.onEnrollmentProgress(getSensorId(), remaining));
if (remaining == 0) {
- resetIgnoreDisplayTouches();
mSensorOverlays.hide(getSensorId());
mAuthenticationStateListeners.onAuthenticationStopped(
new AuthenticationStoppedInfo.Builder(
@@ -211,7 +210,6 @@
);
super.onError(errorCode, vendorCode);
- resetIgnoreDisplayTouches();
mSensorOverlays.hide(getSensorId());
mAuthenticationStateListeners.onAuthenticationStopped(
new AuthenticationStoppedInfo.Builder(BiometricSourceType.FINGERPRINT,
@@ -227,7 +225,6 @@
@Override
protected void startHalOperation() {
- resetIgnoreDisplayTouches();
mSensorOverlays.show(getSensorId(),
getRequestReasonFromFingerprintEnrollReason(mEnrollReason), this);
mAuthenticationStateListeners.onAuthenticationStarted(new AuthenticationStartedInfo
@@ -277,7 +274,6 @@
@Override
protected void stopHalOperation() {
- resetIgnoreDisplayTouches();
mSensorOverlays.hide(getSensorId());
mAuthenticationStateListeners.onAuthenticationStopped(new AuthenticationStoppedInfo
.Builder(BiometricSourceType.FINGERPRINT,
@@ -359,5 +355,14 @@
}
@Override
+ public void setIgnoreDisplayTouches(boolean ignoreTouches) {
+ try {
+ getFreshDaemon().getSession().setIgnoreDisplayTouches(ignoreTouches);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to send setIgnoreDisplayTouches", e);
+ }
+ }
+
+ @Override
public void onPowerPressed() {}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 1bddb83b..12baf00 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -790,6 +790,19 @@
}
@Override
+ public void setIgnoreDisplayTouches(long requestId, int sensorId, boolean ignoreTouches) {
+ mFingerprintSensors.get(sensorId).getScheduler().getCurrentClientIfMatches(
+ requestId, (client) -> {
+ if (!(client instanceof Udfps)) {
+ Slog.e(getTag(),
+ "setIgnoreDisplayTouches received during client: " + client);
+ return;
+ }
+ ((Udfps) client).setIgnoreDisplayTouches(ignoreTouches);
+ });
+ }
+
+ @Override
public void onPowerPressed() {
for (int i = 0; i < mFingerprintSensors.size(); i++) {
final Sensor sensor = mFingerprintSensors.valueAt(i);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 5f1bc2a..6928b33 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1947,6 +1947,27 @@
}
}
+ private void setVirtualDisplayRotationInternal(IBinder appToken,
+ @Surface.Rotation int rotation) {
+ int displayId;
+ synchronized (mSyncRoot) {
+ if (mVirtualDisplayAdapter == null) {
+ return;
+ }
+ DisplayDevice device = mVirtualDisplayAdapter.getDisplayDevice(appToken);
+ if (device == null) {
+ return;
+ }
+ LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
+ if (display == null) {
+ return;
+ }
+ displayId = display.getDisplayIdLocked();
+ }
+ mWindowManagerInternal.setNonDefaultDisplayRotation(
+ displayId, rotation, /* caller= */ "Virtual Display");
+ }
+
private void registerDefaultDisplayAdapters() {
// Register default display adapters.
synchronized (mSyncRoot) {
@@ -4199,6 +4220,20 @@
}
@Override // Binder call
+ public void setVirtualDisplayRotation(IVirtualDisplayCallback callback,
+ @Surface.Rotation int rotation) {
+ if (!android.companion.virtualdevice.flags.Flags.virtualDisplayRotationApi()) {
+ return;
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ setVirtualDisplayRotationInternal(callback.asBinder(), rotation);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
public void dump(@NonNull FileDescriptor fd, @NonNull final PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 1a5c79f..9b02f4b 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -56,6 +56,7 @@
import android.util.ArrayMap;
import android.util.Slog;
import android.view.Display;
+import android.view.DisplayCutout;
import android.view.DisplayShape;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -213,6 +214,10 @@
}
}
+ DisplayDevice getDisplayDevice(IBinder appToken) {
+ return mVirtualDisplayDevices.get(appToken);
+ }
+
/**
* Generates a virtual display's unique identifier.
*
@@ -271,6 +276,7 @@
private boolean mIsDisplayOn;
private int mDisplayIdToMirror;
private boolean mIsWindowManagerMirroring;
+ private DisplayCutout mDisplayCutout;
public VirtualDisplayDevice(IBinder displayToken, IBinder appToken,
int ownerUid, String ownerPackageName, Surface surface, int flags,
@@ -286,6 +292,7 @@
mHeight = virtualDisplayConfig.getHeight();
mDensityDpi = virtualDisplayConfig.getDensityDpi();
mRequestedRefreshRate = virtualDisplayConfig.getRequestedRefreshRate();
+ mDisplayCutout = virtualDisplayConfig.getDisplayCutout();
mMode = createMode(mWidth, mHeight, getRefreshRate());
mSurface = surface;
mFlags = flags;
@@ -567,6 +574,7 @@
mInfo.displayShape =
DisplayShape.createDefaultDisplayShape(mInfo.width, mInfo.height, false);
+ mInfo.displayCutout = mDisplayCutout;
}
return mInfo;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 0f9c344..49888db 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -597,6 +597,12 @@
@Constants.HandleMessageResult
protected int handleReportPhysicalAddress(HdmiCecMessage message) {
super.handleReportPhysicalAddress(message);
+ // Ignore <Report Physical Address> while DeviceDiscoveryAction is in progress to avoid
+ // starting a NewDeviceAction which might interfere in creating the list of known devices.
+ if (hasAction(DeviceDiscoveryAction.class)) {
+ return Constants.HANDLED;
+ }
+
int path = HdmiUtils.twoBytesToInt(message.getParams());
int address = message.getSource();
int type = message.getParams()[2];
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index bef984b..1f46af8 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -3364,6 +3364,10 @@
mPointerIconCache.setPointerFillStyle(fillStyle);
}
+ void setPointerStrokeStyle(@PointerIcon.PointerIconVectorStyleStroke int strokeStyle) {
+ mPointerIconCache.setPointerStrokeStyle(strokeStyle);
+ }
+
void setPointerScale(float scale) {
mPointerIconCache.setPointerScale(scale);
}
diff --git a/services/core/java/com/android/server/input/InputSettingsObserver.java b/services/core/java/com/android/server/input/InputSettingsObserver.java
index 593b091..000f312 100644
--- a/services/core/java/com/android/server/input/InputSettingsObserver.java
+++ b/services/core/java/com/android/server/input/InputSettingsObserver.java
@@ -18,6 +18,7 @@
import static android.view.PointerIcon.DEFAULT_POINTER_SCALE;
import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_BLACK;
+import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_STROKE_WHITE;
import static android.view.flags.Flags.enableVectorCursorA11ySettings;
import static com.android.input.flags.Flags.rateLimitUserActivityPokeInDispatcher;
@@ -103,6 +104,8 @@
(reason) -> updateStylusPointerIconEnabled()),
Map.entry(Settings.System.getUriFor(Settings.System.POINTER_FILL_STYLE),
(reason) -> updatePointerFillStyleFromSettings()),
+ Map.entry(Settings.System.getUriFor(Settings.System.POINTER_STROKE_STYLE),
+ (reason) -> updatePointerStrokeStyleFromSettings()),
Map.entry(Settings.System.getUriFor(Settings.System.POINTER_SCALE),
(reason) -> updatePointerScaleFromSettings()));
}
@@ -281,6 +284,17 @@
mService.setPointerFillStyle(pointerFillStyle);
}
+ private void updatePointerStrokeStyleFromSettings() {
+ if (!enableVectorCursorA11ySettings()) {
+ return;
+ }
+ final int pointerStrokeStyle = Settings.System.getIntForUser(
+ mContext.getContentResolver(), Settings.System.POINTER_STROKE_STYLE,
+ POINTER_ICON_VECTOR_STYLE_STROKE_WHITE,
+ UserHandle.USER_CURRENT);
+ mService.setPointerStrokeStyle(pointerStrokeStyle);
+ }
+
private void updatePointerScaleFromSettings() {
if (!enableVectorCursorA11ySettings()) {
return;
diff --git a/services/core/java/com/android/server/input/PointerIconCache.java b/services/core/java/com/android/server/input/PointerIconCache.java
index 44622d8..297cd68 100644
--- a/services/core/java/com/android/server/input/PointerIconCache.java
+++ b/services/core/java/com/android/server/input/PointerIconCache.java
@@ -18,6 +18,7 @@
import static android.view.PointerIcon.DEFAULT_POINTER_SCALE;
import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_BLACK;
+import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_STROKE_WHITE;
import android.annotation.NonNull;
import android.content.Context;
@@ -65,6 +66,9 @@
private @PointerIcon.PointerIconVectorStyleFill int mPointerIconFillStyle =
POINTER_ICON_VECTOR_STYLE_FILL_BLACK;
@GuardedBy("mLoadedPointerIconsByDisplayAndType")
+ private @PointerIcon.PointerIconVectorStyleStroke int mPointerIconStrokeStyle =
+ POINTER_ICON_VECTOR_STYLE_STROKE_WHITE;
+ @GuardedBy("mLoadedPointerIconsByDisplayAndType")
private float mPointerIconScale = DEFAULT_POINTER_SCALE;
private final DisplayManager.DisplayListener mDisplayListener =
@@ -120,6 +124,11 @@
mUiThreadHandler.post(() -> handleSetPointerFillStyle(fillStyle));
}
+ /** Set the stroke style for vector pointer icons. */
+ public void setPointerStrokeStyle(@PointerIcon.PointerIconVectorStyleStroke int strokeStyle) {
+ mUiThreadHandler.post(() -> handleSetPointerStrokeStyle(strokeStyle));
+ }
+
/** Set the scale for vector pointer icons. */
public void setPointerScale(float scale) {
mUiThreadHandler.post(() -> handleSetPointerScale(scale));
@@ -144,6 +153,8 @@
theme.setTo(context.getTheme());
theme.applyStyle(PointerIcon.vectorFillStyleToResource(mPointerIconFillStyle),
/* force= */ true);
+ theme.applyStyle(PointerIcon.vectorStrokeStyleToResource(mPointerIconStrokeStyle),
+ /* force= */ true);
icon = PointerIcon.getLoadedSystemIcon(new ContextThemeWrapper(context, theme),
type, mUseLargePointerIcons, mPointerIconScale);
iconsByType.put(type, icon);
@@ -224,6 +235,20 @@
}
@android.annotation.UiThread
+ private void handleSetPointerStrokeStyle(
+ @PointerIcon.PointerIconVectorStyleStroke int strokeStyle) {
+ synchronized (mLoadedPointerIconsByDisplayAndType) {
+ if (mPointerIconStrokeStyle == strokeStyle) {
+ return;
+ }
+ mPointerIconStrokeStyle = strokeStyle;
+ // Clear all cached icons on all displays.
+ mLoadedPointerIconsByDisplayAndType.clear();
+ }
+ mNative.reloadPointerIcons();
+ }
+
+ @android.annotation.UiThread
private void handleSetPointerScale(float scale) {
synchronized (mLoadedPointerIconsByDisplayAndType) {
if (mPointerIconScale == scale) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 89b320d..fbb6ccf 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -195,6 +195,7 @@
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -298,22 +299,21 @@
private final String[] mNonPreemptibleInputMethods;
/**
- * See {@link #shouldEnableExperimentalConcurrentMultiUserMode(Context)} about when set to be
- * {@code true}.
+ * See {@link #shouldEnableConcurrentMultiUserMode(Context)} about when set to be {@code true}.
*/
@SharedByAllUsersField
- private final boolean mExperimentalConcurrentMultiUserModeEnabled;
+ private final boolean mConcurrentMultiUserModeEnabled;
/**
- * Returns {@code true} if experimental concurrent multi-user mode is enabled.
+ * Returns {@code true} if the concurrent multi-user mode is enabled.
*
* <p>Currently not compatible with profiles (e.g. work profile).</p>
*
* @param context {@link Context} to be used to query
* {@link PackageManager#FEATURE_AUTOMOTIVE}
- * @return {@code true} if experimental concurrent multi-user mode is enabled.
+ * @return {@code true} if the concurrent multi-user mode is enabled.
*/
- static boolean shouldEnableExperimentalConcurrentMultiUserMode(@NonNull Context context) {
+ static boolean shouldEnableConcurrentMultiUserMode(@NonNull Context context) {
return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
&& UserManager.isVisibleBackgroundUsersEnabled()
&& context.getResources().getBoolean(android.R.bool.config_perDisplayFocusEnabled)
@@ -330,7 +330,7 @@
@UserIdInt
@BinderThread
private int resolveImeUserIdLocked(@UserIdInt int callingProcessUserId) {
- return mExperimentalConcurrentMultiUserModeEnabled ? callingProcessUserId : mCurrentUserId;
+ return mConcurrentMultiUserModeEnabled ? callingProcessUserId : mCurrentUserId;
}
final Context mContext;
@@ -571,10 +571,6 @@
private final ImeTrackerService mImeTrackerService;
class SettingsObserver extends ContentObserver {
- int mUserId;
- boolean mRegistered = false;
- @NonNull
- String mLastEnabled = "";
/**
* <em>This constructor must be called within the lock.</em>
@@ -583,37 +579,29 @@
super(handler);
}
- @GuardedBy("ImfLock.class")
- public void registerContentObserverLocked(@UserIdInt int userId) {
- if (mRegistered && mUserId == userId) {
- return;
- }
+ void registerContentObserverForAllUsers() {
ContentResolver resolver = mContext.getContentResolver();
- if (mRegistered) {
- mContext.getContentResolver().unregisterContentObserver(this);
- mRegistered = false;
- }
- if (mUserId != userId) {
- mLastEnabled = "";
- mUserId = userId;
- }
- resolver.registerContentObserver(Settings.Secure.getUriFor(
- Settings.Secure.DEFAULT_INPUT_METHOD), false, this, userId);
- resolver.registerContentObserver(Settings.Secure.getUriFor(
- Settings.Secure.ENABLED_INPUT_METHODS), false, this, userId);
- resolver.registerContentObserver(Settings.Secure.getUriFor(
- Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this, userId);
- resolver.registerContentObserver(Settings.Secure.getUriFor(
- Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD), false, this, userId);
- resolver.registerContentObserver(Settings.Secure.getUriFor(
- Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE), false, this, userId);
- resolver.registerContentObserver(Settings.Secure.getUriFor(
- STYLUS_HANDWRITING_ENABLED), false, this);
- mRegistered = true;
+ resolver.registerContentObserverAsUser(Settings.Secure.getUriFor(
+ Settings.Secure.DEFAULT_INPUT_METHOD), false, this, UserHandle.ALL);
+ resolver.registerContentObserverAsUser(Settings.Secure.getUriFor(
+ Settings.Secure.ENABLED_INPUT_METHODS), false, this, UserHandle.ALL);
+ resolver.registerContentObserverAsUser(Settings.Secure.getUriFor(
+ Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this, UserHandle.ALL);
+ resolver.registerContentObserverAsUser(Settings.Secure.getUriFor(
+ Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD), false, this, UserHandle.ALL);
+ resolver.registerContentObserverAsUser(Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE), false, this, UserHandle.ALL);
+ resolver.registerContentObserverAsUser(Settings.Secure.getUriFor(
+ STYLUS_HANDWRITING_ENABLED), false, this, UserHandle.ALL);
}
@Override
- public void onChange(boolean selfChange, Uri uri) {
+ public void onChange(boolean selfChange, @NonNull Collection<Uri> uris, int flags,
+ @UserIdInt int userId) {
+ uris.forEach(uri -> onChangeInternal(uri, userId));
+ }
+
+ private void onChangeInternal(@NonNull Uri uri, @UserIdInt int userId) {
final Uri showImeUri = Settings.Secure.getUriFor(
Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD);
final Uri accessibilityRequestingNoImeUri = Settings.Secure.getUriFor(
@@ -621,23 +609,27 @@
final Uri stylusHandwritingEnabledUri = Settings.Secure.getUriFor(
STYLUS_HANDWRITING_ENABLED);
synchronized (ImfLock.class) {
+ if (!mConcurrentMultiUserModeEnabled && mCurrentUserId != userId) {
+ return;
+ }
+
if (showImeUri.equals(uri)) {
mMenuController.updateKeyboardFromSettingsLocked();
} else if (accessibilityRequestingNoImeUri.equals(uri)) {
final int accessibilitySoftKeyboardSetting = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0, mUserId);
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0, userId);
mVisibilityStateComputer.getImePolicy().setA11yRequestNoSoftKeyboard(
accessibilitySoftKeyboardSetting);
- final var userData = getUserData(mUserId);
+ final var userData = getUserData(userId);
if (mVisibilityStateComputer.getImePolicy().isA11yRequestNoSoftKeyboard()) {
hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow,
0 /* flags */, SoftInputShowHideReason.HIDE_SETTINGS_ON_CHANGE,
- mUserId);
- } else if (isShowRequestedForCurrentWindow(mUserId)) {
+ userId);
+ } else if (isShowRequestedForCurrentWindow(userId)) {
showCurrentInputLocked(userData.mImeBindingState.mFocusedWindow,
InputMethodManager.SHOW_IMPLICIT,
- SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE, mUserId);
+ SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE, userId);
}
} else if (stylusHandwritingEnabledUri.equals(uri)) {
InputMethodManager.invalidateLocalStylusHandwritingAvailabilityCaches();
@@ -645,22 +637,17 @@
.invalidateLocalConnectionlessStylusHandwritingAvailabilityCaches();
} else {
boolean enabledChanged = false;
- String newEnabled = InputMethodSettingsRepository.get(mUserId)
+ String newEnabled = InputMethodSettingsRepository.get(userId)
.getEnabledInputMethodsStr();
- if (!mLastEnabled.equals(newEnabled)) {
- mLastEnabled = newEnabled;
+ final var userData = getUserData(userId);
+ if (!userData.mLastEnabledInputMethodsStr.equals(newEnabled)) {
+ userData.mLastEnabledInputMethodsStr = newEnabled;
enabledChanged = true;
}
- updateInputMethodsFromSettingsLocked(enabledChanged, mUserId);
+ updateInputMethodsFromSettingsLocked(enabledChanged, userId);
}
}
}
-
- @Override
- public String toString() {
- return "SettingsObserver{mUserId=" + mUserId + " mRegistered=" + mRegistered
- + " mLastEnabled=" + mLastEnabled + "}";
- }
}
/**
@@ -973,7 +960,7 @@
public Lifecycle(Context context) {
this(context, new InputMethodManagerService(context,
- shouldEnableExperimentalConcurrentMultiUserMode(context)));
+ shouldEnableConcurrentMultiUserMode(context)));
}
public Lifecycle(
@@ -1003,7 +990,7 @@
public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {
// Called on ActivityManager thread.
synchronized (ImfLock.class) {
- if (mService.mExperimentalConcurrentMultiUserModeEnabled) {
+ if (mService.mConcurrentMultiUserModeEnabled) {
// In concurrent multi-user mode, we in general do not rely on the concept of
// current user.
return;
@@ -1037,9 +1024,9 @@
SecureSettingsWrapper.onUserStarting(userId);
synchronized (ImfLock.class) {
mService.getUserData(userId);
- if (mService.mExperimentalConcurrentMultiUserModeEnabled) {
+ if (mService.mConcurrentMultiUserModeEnabled) {
if (mService.mCurrentUserId != userId && mService.mSystemReady) {
- mService.experimentalInitializeVisibleBackgroundUserLocked(userId);
+ mService.initializeVisibleBackgroundUserLocked(userId);
}
}
}
@@ -1062,8 +1049,8 @@
// We need to rebuild IMEs.
postInputMethodSettingUpdatedLocked(false /* resetDefaultEnabledIme */, userId);
updateInputMethodsFromSettingsLocked(true /* enabledChanged */, userId);
- } else if (mExperimentalConcurrentMultiUserModeEnabled) {
- experimentalInitializeVisibleBackgroundUserLocked(userId);
+ } else if (mConcurrentMultiUserModeEnabled) {
+ initializeVisibleBackgroundUserLocked(userId);
}
}
}
@@ -1090,20 +1077,19 @@
}
public InputMethodManagerService(Context context,
- boolean experimentalConcurrentMultiUserModeEnabled) {
- this(context, experimentalConcurrentMultiUserModeEnabled, null, null, null);
+ boolean concurrentMultiUserModeEnabled) {
+ this(context, concurrentMultiUserModeEnabled, null, null, null);
}
@VisibleForTesting
InputMethodManagerService(
Context context,
- boolean experimentalConcurrentMultiUserModeEnabled,
+ boolean concurrentMultiUserModeEnabled,
@Nullable ServiceThread serviceThreadForTesting,
@Nullable ServiceThread ioThreadForTesting,
@Nullable IntFunction<InputMethodBindingController> bindingControllerForTesting) {
synchronized (ImfLock.class) {
- mExperimentalConcurrentMultiUserModeEnabled =
- experimentalConcurrentMultiUserModeEnabled;
+ mConcurrentMultiUserModeEnabled = concurrentMultiUserModeEnabled;
mContext = context;
mRes = context.getResources();
SecureSettingsWrapper.onStart(mContext);
@@ -1307,11 +1293,12 @@
maybeInitImeNavbarConfigLocked(newUserId);
- // ContentObserver should be registered again when the user is changed
- mSettingsObserver.registerContentObserverLocked(newUserId);
+ final var newUserData = getUserData(newUserId);
+
+ // TODO(b/342027196): Double check if we need to always reset upon user switching.
+ newUserData.mLastEnabledInputMethodsStr = "";
mCurrentUserId = newUserId;
- final var newUserData = getUserData(newUserId);
final String defaultImiId = SecureSettingsWrapper.getString(
Settings.Secure.DEFAULT_INPUT_METHOD, null, newUserId);
@@ -1402,7 +1389,7 @@
}, "Lazily initialize IMMS#mImeDrawsImeNavBarRes");
mMyPackageMonitor.register(mContext, UserHandle.ALL, mIoHandler);
- mSettingsObserver.registerContentObserverLocked(currentUserId);
+ mSettingsObserver.registerContentObserverForAllUsers();
final IntentFilter broadcastFilterForAllUsers = new IntentFilter();
broadcastFilterForAllUsers.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
@@ -1428,10 +1415,10 @@
AdditionalSubtypeMapRepository::startWriterThread,
"Start AdditionalSubtypeMapRepository's writer thread");
- if (mExperimentalConcurrentMultiUserModeEnabled) {
+ if (mConcurrentMultiUserModeEnabled) {
for (int userId : mUserManagerInternal.getUserIds()) {
if (userId != mCurrentUserId) {
- experimentalInitializeVisibleBackgroundUserLocked(userId);
+ initializeVisibleBackgroundUserLocked(userId);
}
}
}
@@ -2538,7 +2525,7 @@
@SuppressWarnings("GuardedBy") Consumer<ClientState> clearClientSession = c -> {
// TODO(b/305849394): Figure out what we should do for single user IME mode.
final boolean shouldClearClientSession =
- !mExperimentalConcurrentMultiUserModeEnabled
+ !mConcurrentMultiUserModeEnabled
|| UserHandle.getUserId(c.mUid) == userId;
if (shouldClearClientSession) {
clearClientSessionLocked(c);
@@ -2840,27 +2827,25 @@
}
/**
- * This is an experimental implementation used when and only when
- * {@link #mExperimentalConcurrentMultiUserModeEnabled}.
+ * This initialization logic is used when and only when {@link #mConcurrentMultiUserModeEnabled}
+ * is set to {@code true}.
*
- * <p>Never assume what this method is doing is officially supported. For the canonical and
- * desired behaviors always refer to single-user code paths such as
+ * <p>There remain several yet-to-be-implemented features. For the canonical and desired
+ * behaviors always refer to single-user code paths such as
* {@link #updateInputMethodsFromSettingsLocked(boolean, int)}.</p>
*
* <p>Here are examples of missing features.</p>
* <ul>
- * <li>Subtypes are not supported at all!</li>
* <li>Profiles are not supported.</li>
* <li>
* {@link PackageManager#COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED} is not updated.
* </li>
* <li>{@link InputMethodBindingController#getDeviceIdToShowIme()} is ignored.</li>
- * <li>{@link #mPreventImeStartupUnlessTextEditor} is ignored.</li>
* <li>and so on.</li>
* </ul>
*/
@GuardedBy("ImfLock.class")
- void experimentalInitializeVisibleBackgroundUserLocked(@UserIdInt int userId) {
+ void initializeVisibleBackgroundUserLocked(@UserIdInt int userId) {
final var settings = InputMethodSettingsRepository.get(userId);
// Until we figure out what makes most sense, we enable all the pre-installed IMEs in
@@ -2868,7 +2853,7 @@
String enabledImeIdsStr = settings.getEnabledInputMethodsStr();
for (var imi : settings.getMethodList()) {
if (!imi.isSystem()) {
- return;
+ continue;
}
enabledImeIdsStr = InputMethodUtils.concatEnabledImeIds(enabledImeIdsStr, imi.getId());
}
@@ -2881,19 +2866,18 @@
if (TextUtils.isEmpty(id)) {
final InputMethodInfo imi = InputMethodInfoUtils.getMostApplicableDefaultIME(
settings.getEnabledInputMethodList());
- if (imi == null) {
- return;
+ if (imi != null) {
+ id = imi.getId();
+ settings.putSelectedInputMethod(id);
}
- id = imi.getId();
- settings.putSelectedInputMethod(id);
}
+ final var bindingController = getInputMethodBindingController(userId);
+ bindingController.setSelectedMethodId(id);
+ // Also re-initialize controllers.
final var userData = getUserData(userId);
userData.mSwitchingController.resetCircularListLocked(mContext, settings);
userData.mHardwareKeyboardShortcutController.update(settings);
-
- final var bindingController = getInputMethodBindingController(userId);
- bindingController.setSelectedMethodId(id);
}
@GuardedBy("ImfLock.class")
@@ -3701,8 +3685,7 @@
final long ident = Binder.clearCallingIdentity();
try {
// Verify if IMMS is in the process of switching user.
- if (!mExperimentalConcurrentMultiUserModeEnabled
- && mUserSwitchHandlerTask != null) {
+ if (!mConcurrentMultiUserModeEnabled && mUserSwitchHandlerTask != null) {
// There is already an on-going pending user switch task.
final int nextUserId = mUserSwitchHandlerTask.mToUserId;
if (userId == nextUserId) {
@@ -3757,7 +3740,7 @@
}
// Verify if caller is a background user.
- if (!mExperimentalConcurrentMultiUserModeEnabled && userId != mCurrentUserId) {
+ if (!mConcurrentMultiUserModeEnabled && userId != mCurrentUserId) {
if (ArrayUtils.contains(
mUserManagerInternal.getProfileIds(mCurrentUserId, false),
userId)) {
@@ -4269,9 +4252,8 @@
}
if (currentUser) {
// To avoid unnecessary "updateInputMethodsFromSettingsLocked" from happening.
- if (mSettingsObserver != null) {
- mSettingsObserver.mLastEnabled = settings.getEnabledInputMethodsStr();
- }
+ final var userData = getUserData(userId);
+ userData.mLastEnabledInputMethodsStr = settings.getEnabledInputMethodsStr();
updateInputMethodsFromSettingsLocked(false /* enabledChanged */, userId);
}
}
@@ -5539,7 +5521,7 @@
@GuardedBy("ImfLock.class")
private boolean switchToInputMethodLocked(String imeId, @UserIdInt int userId) {
final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
- if (userId == mCurrentUserId) {
+ if (mConcurrentMultiUserModeEnabled || userId == mCurrentUserId) {
if (!settings.getMethodMap().containsKey(imeId)
|| !settings.getEnabledInputMethodList()
.contains(settings.getMethodMap().get(imeId))) {
@@ -6110,6 +6092,8 @@
p.println(" inFullscreenMode=" + u.mInFullscreenMode);
p.println(" switchingController:");
u.mSwitchingController.dump(p, " ");
+ p.println(" mLastEnabledInputMethodsStr="
+ + u.mLastEnabledInputMethodsStr);
};
mUserDataRepository.forAllUserData(userDataDump);
@@ -6123,11 +6107,9 @@
mVisibilityStateComputer.dump(pw, " ");
p.println(" mInFullscreenMode=" + userData.mInFullscreenMode);
p.println(" mSystemReady=" + mSystemReady + " mInteractive=" + mIsInteractive);
- p.println(" mExperimentalConcurrentMultiUserModeEnabled="
- + mExperimentalConcurrentMultiUserModeEnabled);
+ p.println(" mConcurrentMultiUserModeEnabled=" + mConcurrentMultiUserModeEnabled);
p.println(" ENABLE_HIDE_IME_CAPTION_BAR="
+ InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR);
- p.println(" mSettingsObserver=" + mSettingsObserver);
p.println(" mStylusIds=" + (mStylusIds != null
? Arrays.toString(mStylusIds.toArray()) : ""));
diff --git a/services/core/java/com/android/server/inputmethod/UserDataRepository.java b/services/core/java/com/android/server/inputmethod/UserDataRepository.java
index 48284fb..59411ad 100644
--- a/services/core/java/com/android/server/inputmethod/UserDataRepository.java
+++ b/services/core/java/com/android/server/inputmethod/UserDataRepository.java
@@ -174,6 +174,13 @@
mEnabledAccessibilitySessions = new SparseArray<>();
/**
+ * A per-user cache of {@link InputMethodSettings#getEnabledInputMethodsStr()}.
+ */
+ @GuardedBy("ImfLock.class")
+ @NonNull
+ String mLastEnabledInputMethodsStr = "";
+
+ /**
* Intended to be instantiated only from this file.
*/
private UserData(@UserIdInt int userId,
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java b/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java
index 7c1a5e1..93ef6f0 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java
@@ -56,6 +56,7 @@
private final PackageManager mPackageManager;
private final ArrayList<MediaRoute2ProviderServiceProxy> mProxies = new ArrayList<>();
+ private final Runnable mScanPackagesRunnable = this::scanPackages;
private boolean mRunning;
MediaRoute2ProviderWatcher(Context context,
@@ -106,7 +107,7 @@
mRunning = false;
mContext.unregisterReceiver(mScanPackagesReceiver);
- mHandler.removeCallbacks(this::scanPackages);
+ mHandler.removeCallbacks(mScanPackagesRunnable);
// Stop all providers.
for (int i = mProxies.size() - 1; i >= 0; i--) {
@@ -189,8 +190,8 @@
}
private void postScanPackagesIfNeeded() {
- if (!mHandler.hasCallbacks(this::scanPackages)) {
- mHandler.post(this::scanPackages);
+ if (!mHandler.hasCallbacks(mScanPackagesRunnable)) {
+ mHandler.post(mScanPackagesRunnable);
}
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 173fc5c..009e9b8 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -4568,7 +4568,7 @@
PackageManagerException.INTERNAL_ERROR_SYSTEM_OVERLAY_STATIC);
}
} else {
- if ((scanFlags & SCAN_AS_VENDOR) != 0) {
+ if ((scanFlags & (SCAN_AS_VENDOR | SCAN_AS_ODM)) != 0) {
if (pkg.getTargetSdkVersion() < ScanPackageUtils.getVendorPartitionVersion()) {
Slog.w(TAG, "System overlay " + pkg.getPackageName()
+ " targets an SDK below the required SDK level of vendor"
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c0b8034..2e63cdb 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -186,6 +186,7 @@
import com.android.internal.pm.pkg.component.ParsedMainComponent;
import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
import com.android.internal.telephony.CarrierAppUtils;
+import com.android.internal.telephony.TelephonyPermissions;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.ConcurrentUtils;
@@ -4492,8 +4493,7 @@
void setSystemAppHiddenUntilInstalled(@NonNull Computer snapshot, String packageName,
boolean hidden) {
final int callingUid = Binder.getCallingUid();
- final boolean calledFromSystemOrPhone = callingUid == Process.PHONE_UID
- || callingUid == Process.SYSTEM_UID;
+ final boolean calledFromSystemOrPhone = TelephonyPermissions.isSystemOrPhone(callingUid);
if (!calledFromSystemOrPhone) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS,
"setSystemAppHiddenUntilInstalled");
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index ff8abf8..924b36c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -92,6 +92,7 @@
import com.android.internal.content.InstallLocationUtils;
import com.android.internal.content.NativeLibraryHelper;
+import com.android.internal.telephony.TelephonyPermissions;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.HexDump;
@@ -356,7 +357,7 @@
* If not, throws a {@link SecurityException}.
*/
public static void enforceSystemOrPhoneCaller(String methodName, int callingUid) {
- if (callingUid != Process.PHONE_UID && callingUid != Process.SYSTEM_UID) {
+ if (!TelephonyPermissions.isSystemOrPhone(callingUid)) {
throw new SecurityException(
"Cannot call " + methodName + " from UID " + callingUid);
}
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 4d07ab5..8be20b0 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -1637,10 +1637,12 @@
private boolean isSystemOrCertificateMatchingPackage(PackageInfo pi, String cert) {
if (cert == null) {
return pi.applicationInfo.isSystemApp();
+ } else if (Objects.equals(cert, "platform")) {
+ return mServiceInternal.isPlatformSigned(pi.packageName);
+ } else {
+ return mContext.getPackageManager().hasSigningCertificate(pi.packageName, HexEncoding.
+ decode(cert.replace(":", "")), PackageManager.CERT_INPUT_SHA256);
}
-
- return mContext.getPackageManager().hasSigningCertificate(pi.packageName, HexEncoding.
- decode(cert.replace(":", "")), PackageManager.CERT_INPUT_SHA256);
}
private static boolean doesPackageSupportRuntimePermissions(PackageInfo pkg) {
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index 8f39ffb..685ab3a 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -16,6 +16,8 @@
package com.android.server.rollback;
+import static android.crashrecovery.flags.Flags.deprecateFlagsAndSettingsResets;
+
import static com.android.server.rollback.RollbackManagerServiceImpl.sendFailure;
import android.Manifest;
@@ -623,8 +625,10 @@
parentSession.addChildSessionId(sessionId);
}
- // Clear flags.
- RescueParty.resetDeviceConfigForPackages(packageNames);
+ if (!deprecateFlagsAndSettingsResets()) {
+ // Clear flags.
+ RescueParty.resetDeviceConfigForPackages(packageNames);
+ }
Consumer<Intent> onResult = result -> {
mHandler.post(() -> {
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index 2fc183d..bc29b37 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -532,7 +532,8 @@
return false;
}
- if (Flags.keyboardCategoryEnabled() && mVibrationConfig.hasFixedKeyboardAmplitude()) {
+ if (Flags.keyboardCategoryEnabled()
+ && mVibrationConfig.isKeyboardVibrationSettingsSupported()) {
int category = callerInfo.attrs.getCategory();
if (usage == USAGE_TOUCH && category == CATEGORY_KEYBOARD) {
// Keyboard touch has a different user setting.
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 484481b..8be3380 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -734,6 +734,9 @@
*/
private boolean mOccludesParent;
+ /** Whether the activity have style floating */
+ private boolean mStyleFloating;
+
/**
* Unlike {@link #mOccludesParent} which can be changed at runtime. This is a static attribute
* from the style of activity. Because we don't want {@link WindowContainer#getOrientation()}
@@ -2188,7 +2191,11 @@
realTheme, com.android.internal.R.styleable.Window, mUserId);
if (ent != null) {
- mOccludesParent = !ActivityInfo.isTranslucentOrFloating(ent.array)
+ final boolean styleTranslucent = ent.array.getBoolean(
+ com.android.internal.R.styleable.Window_windowIsTranslucent, false);
+ mStyleFloating = ent.array.getBoolean(
+ com.android.internal.R.styleable.Window_windowIsFloating, false);
+ mOccludesParent = !(styleTranslucent || mStyleFloating)
// This style is propagated to the main window attributes with
// FLAG_SHOW_WALLPAPER from PhoneWindow#generateLayout.
|| ent.array.getBoolean(R.styleable.Window_windowShowWallpaper, false);
@@ -2197,6 +2204,7 @@
mOptOutEdgeToEdge = ent.array.getBoolean(
R.styleable.Window_windowOptOutEdgeToEdgeEnforcement, false);
} else {
+ mStyleFloating = false;
mStyleFillsParent = mOccludesParent = true;
noDisplay = false;
mOptOutEdgeToEdge = false;
@@ -3237,6 +3245,10 @@
return occludesParent(true /* includingFinishing */);
}
+ boolean isStyleFloating() {
+ return mStyleFloating;
+ }
+
/** Returns true if this activity is not finishing, is opaque and fills the entire space of
* this task. */
boolean occludesParent() {
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index d45ed12..14e256f 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -1335,12 +1335,16 @@
}
// If there is only one adaptor, attach the windowless window to top activity,
// because fixed rotation only applies on activity.
- // Note that embedded activity won't use fixed rotation.
- final Configuration openConfig = mAdaptors.length == 1
+ // Note that embedded activity won't use fixed rotation. Also, there is only one
+ // animation target for closing task.
+ final boolean chooseActivity = mAdaptors.length == 1
+ && (switchType == ACTIVITY_SWITCH || mainActivity.mDisplayContent
+ .isFixedRotationLaunchingApp(mainActivity));
+ final Configuration openConfig = chooseActivity
? mainActivity.getConfiguration() : openTask.getConfiguration();
mRequestedStartingSurfaceId = openTask.mAtmService.mTaskOrganizerController
.addWindowlessStartingSurface(openTask, mainActivity,
- mAdaptors.length == 1 ? mainActivity.getSurfaceControl()
+ chooseActivity ? mainActivity.getSurfaceControl()
: mRemoteAnimationTarget.leash, snapshot, openConfig,
new IWindowlessStartingSurfaceCallback.Stub() {
// Once the starting surface has been created in shell, it will call
diff --git a/services/core/java/com/android/server/wm/DeviceStateController.java b/services/core/java/com/android/server/wm/DeviceStateController.java
index 857e03d..475a504 100644
--- a/services/core/java/com/android/server/wm/DeviceStateController.java
+++ b/services/core/java/com/android/server/wm/DeviceStateController.java
@@ -16,9 +16,19 @@
package com.android.server.wm;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN;
+
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
+import android.hardware.devicestate.DeviceStateManager;
+import android.hardware.devicestate.feature.flags.FeatureFlags;
+import android.hardware.devicestate.feature.flags.FeatureFlagsImpl;
import android.util.ArrayMap;
import android.util.Pair;
@@ -28,6 +38,7 @@
import com.android.internal.util.ArrayUtils;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
@@ -43,16 +54,16 @@
@NonNull
private final WindowManagerGlobalLock mWmLock;
@NonNull
- private final int[] mOpenDeviceStates;
+ private final List<Integer> mOpenDeviceStates;
@NonNull
- private final int[] mHalfFoldedDeviceStates;
+ private final List<Integer> mHalfFoldedDeviceStates;
@NonNull
- private final int[] mFoldedDeviceStates;
+ private final List<Integer> mFoldedDeviceStates;
@NonNull
- private final int[] mRearDisplayDeviceStates;
- private final int mConcurrentDisplayDeviceState;
+ private final List<Integer> mRearDisplayDeviceStates;
+ private final List<Integer> mConcurrentDisplayDeviceStates;
@NonNull
- private final int[] mReverseRotationAroundZAxisStates;
+ private final List<Integer> mReverseRotationAroundZAxisStates;
@GuardedBy("mWmLock")
@NonNull
@VisibleForTesting
@@ -76,18 +87,55 @@
DeviceStateController(@NonNull Context context, @NonNull WindowManagerGlobalLock wmLock) {
mWmLock = wmLock;
- mOpenDeviceStates = context.getResources()
- .getIntArray(R.array.config_openDeviceStates);
- mHalfFoldedDeviceStates = context.getResources()
- .getIntArray(R.array.config_halfFoldedDeviceStates);
- mFoldedDeviceStates = context.getResources()
- .getIntArray(R.array.config_foldedDeviceStates);
- mRearDisplayDeviceStates = context.getResources()
- .getIntArray(R.array.config_rearDisplayDeviceStates);
- mConcurrentDisplayDeviceState = context.getResources()
- .getInteger(R.integer.config_deviceStateConcurrentRearDisplay);
- mReverseRotationAroundZAxisStates = context.getResources()
- .getIntArray(R.array.config_deviceStatesToReverseDefaultDisplayRotationAroundZAxis);
+ final FeatureFlags deviceStateManagerFlags = new FeatureFlagsImpl();
+ if (deviceStateManagerFlags.deviceStatePropertyMigration()) {
+ mOpenDeviceStates = new ArrayList<>();
+ mHalfFoldedDeviceStates = new ArrayList<>();
+ mFoldedDeviceStates = new ArrayList<>();
+ mRearDisplayDeviceStates = new ArrayList<>();
+ mConcurrentDisplayDeviceStates = new ArrayList<>();
+
+ final DeviceStateManager deviceStateManager =
+ context.getSystemService(DeviceStateManager.class);
+ final List<android.hardware.devicestate.DeviceState> deviceStates =
+ deviceStateManager.getSupportedDeviceStates();
+
+ for (int i = 0; i < deviceStates.size(); i++) {
+ final android.hardware.devicestate.DeviceState state = deviceStates.get(i);
+ if (state.hasProperty(
+ PROPERTY_FEATURE_REAR_DISPLAY)) {
+ mRearDisplayDeviceStates.add(state.getIdentifier());
+ } else if (state.hasProperty(
+ PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT)) {
+ mConcurrentDisplayDeviceStates.add(state.getIdentifier());
+ } else if (state.hasProperty(
+ PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY)) {
+ mFoldedDeviceStates.add(state.getIdentifier());
+ } else if (state.hasProperty(
+ PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY)) {
+ if (state.hasProperty(
+ PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN)) {
+ mHalfFoldedDeviceStates.add(state.getIdentifier());
+ } else {
+ mOpenDeviceStates.add(state.getIdentifier());
+ }
+ }
+ }
+ } else {
+ mOpenDeviceStates = copyIntArrayToList(context.getResources()
+ .getIntArray(R.array.config_openDeviceStates));
+ mHalfFoldedDeviceStates = copyIntArrayToList(context.getResources()
+ .getIntArray(R.array.config_halfFoldedDeviceStates));
+ mFoldedDeviceStates = copyIntArrayToList(context.getResources()
+ .getIntArray(R.array.config_foldedDeviceStates));
+ mRearDisplayDeviceStates = copyIntArrayToList(context.getResources()
+ .getIntArray(R.array.config_rearDisplayDeviceStates));
+ mConcurrentDisplayDeviceStates = new ArrayList<>(List.of(context.getResources()
+ .getInteger(R.integer.config_deviceStateConcurrentRearDisplay)));
+ }
+
+ mReverseRotationAroundZAxisStates = copyIntArrayToList(context.getResources().getIntArray(
+ R.array.config_deviceStatesToReverseDefaultDisplayRotationAroundZAxis));
mMatchBuiltInDisplayOrientationToDefaultDisplay = context.getResources()
.getBoolean(R.bool
.config_matchSecondaryInternalDisplaysOrientationToReverseDefaultDisplay);
@@ -145,7 +193,6 @@
*/
public void onDeviceStateReceivedByDisplayManager(int state) {
mCurrentState = state;
-
final DeviceState deviceState;
if (ArrayUtils.contains(mHalfFoldedDeviceStates, state)) {
deviceState = DeviceState.HALF_FOLDED;
@@ -155,9 +202,10 @@
deviceState = DeviceState.REAR;
} else if (ArrayUtils.contains(mOpenDeviceStates, state)) {
deviceState = DeviceState.OPEN;
- } else if (state == mConcurrentDisplayDeviceState) {
+ } else if (ArrayUtils.contains(mConcurrentDisplayDeviceStates, state)) {
deviceState = DeviceState.CONCURRENT;
} else {
+
deviceState = DeviceState.UNKNOWN;
}
@@ -190,4 +238,16 @@
}
return entries;
}
+
+ @NonNull
+ private List<Integer> copyIntArrayToList(@Nullable int[] values) {
+ if (values == null) {
+ return Collections.emptyList();
+ }
+ final List<Integer> valueList = new ArrayList<>();
+ for (int i = 0; i < values.length; i++) {
+ valueList.add(values[i]);
+ }
+ return valueList;
+ }
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 7206b36..33a649b 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3411,6 +3411,7 @@
info.isVisibleRequested = isVisibleRequested();
info.isSleeping = shouldSleepActivities();
info.isTopActivityTransparent = top != null && !top.fillsParent();
+ info.isTopActivityStyleFloating = top != null && top.isStyleFloating();
appCompatTaskInfo.topActivityLetterboxVerticalPosition = TaskInfo.PROPERTY_VALUE_UNSET;
appCompatTaskInfo.topActivityLetterboxHorizontalPosition = TaskInfo.PROPERTY_VALUE_UNSET;
appCompatTaskInfo.topActivityLetterboxWidth = TaskInfo.PROPERTY_VALUE_UNSET;
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 35a7702..f839ed6 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1694,7 +1694,7 @@
// ActivityRecord#canShowWindows() may reject to show its window. The visibility also
// needs to be updated for STATE_ABORT.
commitVisibleActivities(transaction);
- commitVisibleWallpapers();
+ commitVisibleWallpapers(transaction);
if (mTransactionCompletedListeners != null) {
for (int i = 0; i < mTransactionCompletedListeners.size(); i++) {
@@ -2128,7 +2128,7 @@
/**
* Reset waitingToshow for all wallpapers, and commit the visibility of the visible ones
*/
- private void commitVisibleWallpapers() {
+ private void commitVisibleWallpapers(SurfaceControl.Transaction t) {
boolean showWallpaper = shouldWallpaperBeVisible();
for (int i = mParticipants.size() - 1; i >= 0; --i) {
final WallpaperWindowToken wallpaper = mParticipants.valueAt(i).asWallpaperToken();
@@ -2136,6 +2136,14 @@
if (!wallpaper.isVisible() && wallpaper.isVisibleRequested()) {
wallpaper.commitVisibility(showWallpaper);
}
+ if (showWallpaper && Flags.ensureWallpaperInTransitions()
+ && wallpaper.isVisibleRequested()
+ && getLeashSurface(wallpaper, t) != wallpaper.getSurfaceControl()) {
+ // If on a rotation leash, we need to explicitly show the wallpaper surface
+ // because shell only gets the leash and we don't allow non-transition logic
+ // to touch the surfaces until the transition is over.
+ t.show(wallpaper.getSurfaceControl());
+ }
}
}
}
@@ -2837,6 +2845,13 @@
// Use parent rotation because shell doesn't know the surface is rotated.
endRotation = parent.getWindowConfiguration().getRotation();
}
+ } else if (isWallpaper(target) && Flags.ensureWallpaperInTransitions()
+ && target.getRelativeDisplayRotation() != 0
+ && !target.mTransitionController.useShellTransitionsRotation()) {
+ // If the wallpaper is "fixed-rotated", shell is unaware of this, so use the
+ // "as-if-not-rotating" bounds and rotation
+ change.setEndAbsBounds(parent.getBounds());
+ endRotation = parent.getWindowConfiguration().getRotation();
} else {
change.setEndAbsBounds(bounds);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index a42cb09..2ea1cf8 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -44,6 +44,7 @@
import android.view.InputChannel;
import android.view.MagnificationSpec;
import android.view.RemoteAnimationTarget;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.WindowInfo;
@@ -817,6 +818,16 @@
public abstract Context getTopFocusedDisplayUiContext();
/**
+ * Sets the rotation of a non-default display.
+ *
+ * @param displayId The id of the display
+ * @param rotation The new rotation value.
+ * @param caller The requester of the rotation change, used for bookkeeping.
+ */
+ public abstract void setNonDefaultDisplayRotation(int displayId, @Surface.Rotation int rotation,
+ @NonNull String caller);
+
+ /**
* Sets whether the relevant display content can host the relevant home activity and wallpaper.
*
* @param displayUniqueId The unique ID of the display. Note that the display may not yet be
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 57b8040..0c1ec504 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8362,6 +8362,26 @@
}
@Override
+ public void setNonDefaultDisplayRotation(int displayId, @Surface.Rotation int rotation,
+ @NonNull String caller) {
+ if (displayId == Display.DEFAULT_DISPLAY || displayId == Display.INVALID_DISPLAY) {
+ Slog.w(TAG, "Cannot set rotation for display with id: " + displayId);
+ return;
+ }
+ synchronized (mGlobalLock) {
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ if (displayContent == null) {
+ Slog.w(TAG, "Cannot set rotation for display " + displayId
+ + " due to missing DisplayContent");
+ return;
+ }
+ displayContent.getDisplayRotation().setUserRotation(
+ displayContent.getDisplayRotation().getUserRotationMode(), rotation,
+ caller);
+ }
+ }
+
+ @Override
public void setHomeSupportedOnDisplay(String displayUniqueId, int displayType,
boolean supported) {
final long origId = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index fec1175..de73e6c 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -28,6 +28,7 @@
import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import static android.os.PowerManager.DRAW_WAKE_LOCK;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.util.SequenceUtils.getNextSeq;
import static android.view.SurfaceControl.Transaction;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
@@ -96,7 +97,6 @@
import static android.view.WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME;
import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER;
import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
-import static android.util.SequenceUtils.getNextSeq;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
@@ -249,8 +249,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.KeyInterceptionInfo;
-import com.android.internal.protolog.common.LogLevel;
import com.android.internal.protolog.ProtoLog;
+import com.android.internal.protolog.common.LogLevel;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.ToBooleanFunction;
import com.android.server.policy.WindowManagerPolicy;
@@ -1976,8 +1976,8 @@
*/
boolean isReadyForDisplay() {
final boolean parentAndClientVisible = !isParentWindowHidden()
- && mViewVisibility == View.VISIBLE && mToken.isVisible();
- return mHasSurface && isVisibleByPolicy() && !mDestroying
+ && mViewVisibility == View.VISIBLE;
+ return mHasSurface && isVisibleByPolicy() && !mDestroying && mToken.isVisible()
&& (parentAndClientVisible || isAnimating(TRANSITION | PARENTS));
}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 7a710dc..7649a4e 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -193,7 +193,7 @@
"android.hardware.thermal-V2-ndk",
"android.hardware.tv.input@1.0",
"android.hardware.tv.input-V2-ndk",
- "android.hardware.vibrator-V2-cpp",
+ "android.hardware.vibrator-V2-ndk",
"android.hardware.vibrator@1.0",
"android.hardware.vibrator@1.1",
"android.hardware.vibrator@1.2",
diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
index 4be21d8..2804a10 100644
--- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp
+++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
@@ -16,24 +16,21 @@
#define LOG_TAG "VibratorController"
+#include <aidl/android/hardware/vibrator/IVibrator.h>
#include <android/hardware/vibrator/1.3/IVibrator.h>
-#include <android/hardware/vibrator/IVibrator.h>
-
#include <nativehelper/JNIHelp.h>
+#include <utils/Log.h>
+#include <utils/misc.h>
+#include <vibratorservice/VibratorHalController.h>
+
#include "android_runtime/AndroidRuntime.h"
+#include "com_android_server_vibrator_VibratorManagerService.h"
#include "core_jni_helpers.h"
#include "jni.h"
-#include <utils/Log.h>
-#include <utils/misc.h>
-
-#include <vibratorservice/VibratorHalController.h>
-
-#include "com_android_server_vibrator_VibratorManagerService.h"
-
namespace V1_0 = android::hardware::vibrator::V1_0;
namespace V1_3 = android::hardware::vibrator::V1_3;
-namespace aidl = android::hardware::vibrator;
+namespace Aidl = aidl::android::hardware::vibrator;
namespace android {
@@ -67,29 +64,29 @@
} sRampClassInfo;
static_assert(static_cast<uint8_t>(V1_0::EffectStrength::LIGHT) ==
- static_cast<uint8_t>(aidl::EffectStrength::LIGHT));
+ static_cast<uint8_t>(Aidl::EffectStrength::LIGHT));
static_assert(static_cast<uint8_t>(V1_0::EffectStrength::MEDIUM) ==
- static_cast<uint8_t>(aidl::EffectStrength::MEDIUM));
+ static_cast<uint8_t>(Aidl::EffectStrength::MEDIUM));
static_assert(static_cast<uint8_t>(V1_0::EffectStrength::STRONG) ==
- static_cast<uint8_t>(aidl::EffectStrength::STRONG));
+ static_cast<uint8_t>(Aidl::EffectStrength::STRONG));
static_assert(static_cast<uint8_t>(V1_3::Effect::CLICK) ==
- static_cast<uint8_t>(aidl::Effect::CLICK));
+ static_cast<uint8_t>(Aidl::Effect::CLICK));
static_assert(static_cast<uint8_t>(V1_3::Effect::DOUBLE_CLICK) ==
- static_cast<uint8_t>(aidl::Effect::DOUBLE_CLICK));
-static_assert(static_cast<uint8_t>(V1_3::Effect::TICK) == static_cast<uint8_t>(aidl::Effect::TICK));
-static_assert(static_cast<uint8_t>(V1_3::Effect::THUD) == static_cast<uint8_t>(aidl::Effect::THUD));
-static_assert(static_cast<uint8_t>(V1_3::Effect::POP) == static_cast<uint8_t>(aidl::Effect::POP));
+ static_cast<uint8_t>(Aidl::Effect::DOUBLE_CLICK));
+static_assert(static_cast<uint8_t>(V1_3::Effect::TICK) == static_cast<uint8_t>(Aidl::Effect::TICK));
+static_assert(static_cast<uint8_t>(V1_3::Effect::THUD) == static_cast<uint8_t>(Aidl::Effect::THUD));
+static_assert(static_cast<uint8_t>(V1_3::Effect::POP) == static_cast<uint8_t>(Aidl::Effect::POP));
static_assert(static_cast<uint8_t>(V1_3::Effect::HEAVY_CLICK) ==
- static_cast<uint8_t>(aidl::Effect::HEAVY_CLICK));
+ static_cast<uint8_t>(Aidl::Effect::HEAVY_CLICK));
static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_1) ==
- static_cast<uint8_t>(aidl::Effect::RINGTONE_1));
+ static_cast<uint8_t>(Aidl::Effect::RINGTONE_1));
static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_2) ==
- static_cast<uint8_t>(aidl::Effect::RINGTONE_2));
+ static_cast<uint8_t>(Aidl::Effect::RINGTONE_2));
static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_15) ==
- static_cast<uint8_t>(aidl::Effect::RINGTONE_15));
+ static_cast<uint8_t>(Aidl::Effect::RINGTONE_15));
static_assert(static_cast<uint8_t>(V1_3::Effect::TEXTURE_TICK) ==
- static_cast<uint8_t>(aidl::Effect::TEXTURE_TICK));
+ static_cast<uint8_t>(Aidl::Effect::TEXTURE_TICK));
static std::shared_ptr<vibrator::HalController> findVibrator(int32_t vibratorId) {
vibrator::ManagerHalController* manager =
@@ -155,15 +152,15 @@
std::atomic<int64_t> mCallbackId;
};
-static aidl::BrakingPwle brakingPwle(aidl::Braking braking, int32_t duration) {
- aidl::BrakingPwle pwle;
+static Aidl::BrakingPwle brakingPwle(Aidl::Braking braking, int32_t duration) {
+ Aidl::BrakingPwle pwle;
pwle.braking = braking;
pwle.duration = duration;
return pwle;
}
-static aidl::ActivePwle activePwleFromJavaPrimitive(JNIEnv* env, jobject ramp) {
- aidl::ActivePwle pwle;
+static Aidl::ActivePwle activePwleFromJavaPrimitive(JNIEnv* env, jobject ramp) {
+ Aidl::ActivePwle pwle;
pwle.startAmplitude =
static_cast<float>(env->GetFloatField(ramp, sRampClassInfo.startAmplitude));
pwle.endAmplitude = static_cast<float>(env->GetFloatField(ramp, sRampClassInfo.endAmplitude));
@@ -175,20 +172,20 @@
}
/* Return true if braking is not NONE and the active PWLE starts and ends with zero amplitude. */
-static bool shouldBeReplacedWithBraking(aidl::ActivePwle activePwle, aidl::Braking braking) {
- return (braking != aidl::Braking::NONE) && (activePwle.startAmplitude == 0) &&
+static bool shouldBeReplacedWithBraking(Aidl::ActivePwle activePwle, Aidl::Braking braking) {
+ return (braking != Aidl::Braking::NONE) && (activePwle.startAmplitude == 0) &&
(activePwle.endAmplitude == 0);
}
/* Return true if braking is not NONE and the active PWLE only ends with zero amplitude. */
-static bool shouldAddLastBraking(aidl::ActivePwle lastActivePwle, aidl::Braking braking) {
- return (braking != aidl::Braking::NONE) && (lastActivePwle.startAmplitude > 0) &&
+static bool shouldAddLastBraking(Aidl::ActivePwle lastActivePwle, Aidl::Braking braking) {
+ return (braking != Aidl::Braking::NONE) && (lastActivePwle.startAmplitude > 0) &&
(lastActivePwle.endAmplitude == 0);
}
-static aidl::CompositeEffect effectFromJavaPrimitive(JNIEnv* env, jobject primitive) {
- aidl::CompositeEffect effect;
- effect.primitive = static_cast<aidl::CompositePrimitive>(
+static Aidl::CompositeEffect effectFromJavaPrimitive(JNIEnv* env, jobject primitive) {
+ Aidl::CompositeEffect effect;
+ effect.primitive = static_cast<Aidl::CompositePrimitive>(
env->GetIntField(primitive, sPrimitiveClassInfo.id));
effect.scale = static_cast<float>(env->GetFloatField(primitive, sPrimitiveClassInfo.scale));
effect.delayMs = static_cast<int32_t>(env->GetIntField(primitive, sPrimitiveClassInfo.delay));
@@ -282,8 +279,8 @@
ALOGE("vibratorPerformEffect failed because native wrapper was not initialized");
return -1;
}
- aidl::Effect effectType = static_cast<aidl::Effect>(effect);
- aidl::EffectStrength effectStrength = static_cast<aidl::EffectStrength>(strength);
+ Aidl::Effect effectType = static_cast<Aidl::Effect>(effect);
+ Aidl::EffectStrength effectStrength = static_cast<Aidl::EffectStrength>(strength);
auto callback = wrapper->createCallback(vibrationId);
auto performEffectFn = [effectType, effectStrength, &callback](vibrator::HalWrapper* hal) {
return hal->performEffect(effectType, effectStrength, callback);
@@ -300,7 +297,7 @@
return -1;
}
size_t size = env->GetArrayLength(composition);
- std::vector<aidl::CompositeEffect> effects;
+ std::vector<Aidl::CompositeEffect> effects;
for (size_t i = 0; i < size; i++) {
jobject element = env->GetObjectArrayElement(composition, i);
effects.push_back(effectFromJavaPrimitive(env, element));
@@ -321,13 +318,13 @@
ALOGE("vibratorPerformPwleEffect failed because native wrapper was not initialized");
return -1;
}
- aidl::Braking braking = static_cast<aidl::Braking>(brakingId);
+ Aidl::Braking braking = static_cast<Aidl::Braking>(brakingId);
size_t size = env->GetArrayLength(waveform);
- std::vector<aidl::PrimitivePwle> primitives;
+ std::vector<Aidl::PrimitivePwle> primitives;
std::chrono::milliseconds totalDuration(0);
for (size_t i = 0; i < size; i++) {
jobject element = env->GetObjectArrayElement(waveform, i);
- aidl::ActivePwle activePwle = activePwleFromJavaPrimitive(env, element);
+ Aidl::ActivePwle activePwle = activePwleFromJavaPrimitive(env, element);
if ((i > 0) && shouldBeReplacedWithBraking(activePwle, braking)) {
primitives.push_back(brakingPwle(braking, activePwle.duration));
} else {
@@ -356,8 +353,8 @@
return;
}
auto alwaysOnEnableFn = [id, effect, strength](vibrator::HalWrapper* hal) {
- return hal->alwaysOnEnable(static_cast<int32_t>(id), static_cast<aidl::Effect>(effect),
- static_cast<aidl::EffectStrength>(strength));
+ return hal->alwaysOnEnable(static_cast<int32_t>(id), static_cast<Aidl::Effect>(effect),
+ static_cast<Aidl::EffectStrength>(strength));
};
wrapper->halCall<void>(alwaysOnEnableFn, "alwaysOnEnable");
}
@@ -389,7 +386,7 @@
static_cast<jlong>(info.capabilities.value()));
}
if (info.supportedEffects.isOk()) {
- std::vector<aidl::Effect> effects = info.supportedEffects.value();
+ std::vector<Aidl::Effect> effects = info.supportedEffects.value();
jintArray supportedEffects = env->NewIntArray(effects.size());
env->SetIntArrayRegion(supportedEffects, 0, effects.size(),
reinterpret_cast<jint*>(effects.data()));
@@ -397,7 +394,7 @@
sVibratorInfoBuilderClassInfo.setSupportedEffects, supportedEffects);
}
if (info.supportedBraking.isOk()) {
- std::vector<aidl::Braking> braking = info.supportedBraking.value();
+ std::vector<Aidl::Braking> braking = info.supportedBraking.value();
jintArray supportedBraking = env->NewIntArray(braking.size());
env->SetIntArrayRegion(supportedBraking, 0, braking.size(),
reinterpret_cast<jint*>(braking.data()));
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 215cf2c..791d030 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -2134,14 +2134,20 @@
}
t.traceEnd();
- t.traceBegin("StartVpnManagerService");
- try {
- vpnManager = VpnManagerService.create(context);
- ServiceManager.addService(Context.VPN_MANAGEMENT_SERVICE, vpnManager);
- } catch (Throwable e) {
- reportWtf("starting VPN Manager Service", e);
+ if (!isWatch || !android.server.Flags.allowRemovingVpnService()) {
+ t.traceBegin("StartVpnManagerService");
+ try {
+ vpnManager = VpnManagerService.create(context);
+ ServiceManager.addService(Context.VPN_MANAGEMENT_SERVICE, vpnManager);
+ } catch (Throwable e) {
+ reportWtf("starting VPN Manager Service", e);
+ }
+ t.traceEnd();
+ } else {
+ // VPN management currently does not work in Wear, so skip starting the
+ // VPN manager SystemService.
+ Slog.i(TAG, "Not starting VpnManagerService");
}
- t.traceEnd();
t.traceBegin("StartVcnManagementService");
try {
diff --git a/services/java/com/android/server/flags.aconfig b/services/java/com/android/server/flags.aconfig
index e8aa68c..29f3871 100644
--- a/services/java/com/android/server/flags.aconfig
+++ b/services/java/com/android/server/flags.aconfig
@@ -21,4 +21,11 @@
namespace: "wear_frameworks"
description: "Remove WearableSensingManagerService on Wear"
bug: "340929916"
+}
+
+flag {
+ name: "allow_removing_vpn_service"
+ namespace: "wear_frameworks"
+ description: "Allow removing VpnManagerService"
+ bug: "340928692"
}
\ No newline at end of file
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index 78dbc60..0b7438c 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -2687,7 +2687,7 @@
runtimePermissionChangedUidDevices.getOrPut(uid) { mutableSetOf() } += deviceId
}
- if (permission.hasGids && !wasPermissionGranted && isPermissionGranted) {
+ if (permission.hasGids && (wasPermissionGranted != isPermissionGranted)) {
gidsChangedUids += uid
}
}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
index 80eab11..17d9ef9 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
@@ -233,7 +233,7 @@
Process.THREAD_PRIORITY_FOREGROUND,
true /* allowIo */);
mInputMethodManagerService = new InputMethodManagerService(mContext,
- InputMethodManagerService.shouldEnableExperimentalConcurrentMultiUserMode(mContext),
+ InputMethodManagerService.shouldEnableConcurrentMultiUserMode(mContext),
mServiceThread, mIoThread,
unusedUserId -> mMockInputMethodBindingController);
spyOn(mInputMethodManagerService);
diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
index 3377899..f2acbc3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
@@ -27,7 +27,6 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.RescueParty.DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN;
import static com.android.server.RescueParty.LEVEL_FACTORY_RESET;
-import static com.android.server.RescueParty.RESCUE_LEVEL_FACTORY_RESET;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -36,7 +35,6 @@
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
import android.content.ContentResolver;
import android.content.Context;
@@ -47,6 +45,9 @@
import android.os.RecoverySystem;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.FlagsParameterization;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.DeviceConfig;
import android.provider.Settings;
@@ -61,6 +62,9 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
@@ -77,10 +81,14 @@
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
+
/**
* Test RescueParty.
*/
+@RunWith(Parameterized.class)
public class RescuePartyTest {
+ @Rule
+ public SetFlagsRule mSetFlagsRule;
private static final long CURRENT_NETWORK_TIME_MILLIS = 0L;
private static final String FAKE_NATIVE_NAMESPACE1 = "native1";
private static final String FAKE_NATIVE_NAMESPACE2 = "native2";
@@ -103,9 +111,6 @@
private static final String PROP_DISABLE_FACTORY_RESET_FLAG =
"persist.device_config.configuration.disable_rescue_party_factory_reset";
- @Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
private MockitoSession mSession;
private HashMap<String, String> mSystemSettingsMap;
private HashMap<String, String> mCrashRecoveryPropertiesMap;
@@ -129,6 +134,17 @@
@Captor
private ArgumentCaptor<List<String>> mPackageListCaptor;
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getFlags() {
+ return FlagsParameterization.allCombinationsOf(
+ Flags.FLAG_RECOVERABILITY_DETECTION,
+ Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS);
+ }
+
+ public RescuePartyTest(FlagsParameterization flags) {
+ mSetFlagsRule = new SetFlagsRule(flags);
+ }
+
@Before
public void setUp() throws Exception {
mSession =
@@ -234,10 +250,10 @@
}
@Test
- public void testBootLoopDetectionWithExecutionForAllRescueLevels() {
+ @DisableFlags({Flags.FLAG_RECOVERABILITY_DETECTION,
+ Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS})
+ public void testBootLoop() {
// this is old test where the flag needs to be disabled
- mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
-
RescueParty.onSettingsProviderPublished(mMockContext);
verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver),
any(Executor.class),
@@ -264,10 +280,22 @@
noteBoot(5);
assertTrue(RescueParty.isFactoryResetPropertySet());
}
+ @Test
+ @EnableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
+ public void testBootLoopNoFlags() {
+ // this is old test where the flag needs to be disabled
+ noteBoot(1);
+ assertTrue(RescueParty.isRebootPropertySet());
+
+ setCrashRecoveryPropAttemptingReboot(false);
+ noteBoot(2);
+ assertTrue(RescueParty.isFactoryResetPropertySet());
+ }
@Test
- public void testBootLoopDetectionWithExecutionForAllRescueLevelsRecoverabilityDetection() {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
+ @EnableFlags(Flags.FLAG_RECOVERABILITY_DETECTION)
+ @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
+ public void testBootLoopRecoverability() {
RescueParty.onSettingsProviderPublished(mMockContext);
verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver),
any(Executor.class),
@@ -281,12 +309,14 @@
final String[] expectedAllResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2};
+
noteBoot(1);
noteBoot(2);
assertTrue(RescueParty.isRebootPropertySet());
noteBoot(3);
+
verifyOnlySettingsReset(Settings.RESET_MODE_UNTRUSTED_DEFAULTS);
noteBoot(4);
@@ -301,10 +331,10 @@
}
@Test
- public void testPersistentAppCrashDetectionWithExecutionForAllRescueLevels() {
+ @DisableFlags({Flags.FLAG_RECOVERABILITY_DETECTION,
+ Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS})
+ public void testPersistentAppCrash() {
// this is old test where the flag needs to be disabled
- mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
-
noteAppCrash(1, true);
noteAppCrash(2, true);
noteAppCrash(3, true);
@@ -318,8 +348,21 @@
}
@Test
- public void testPersistentAppCrashDetectionWithExecutionForAllRescueLevelsRecoverability() {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
+ @EnableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
+ public void testPersistentAppCrashNoFlags() {
+ // this is old test where the flag needs to be disabled
+ noteAppCrash(1, true);
+ assertTrue(RescueParty.isRebootPropertySet());
+
+ setCrashRecoveryPropAttemptingReboot(false);
+ noteAppCrash(2, true);
+ assertTrue(RescueParty.isFactoryResetPropertySet());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_RECOVERABILITY_DETECTION)
+ @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
+ public void testPersistentAppCrashRecoverability() {
RescueParty.onSettingsProviderPublished(mMockContext);
verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver),
any(Executor.class),
@@ -357,10 +400,10 @@
}
@Test
- public void testNonPersistentAppDoesntDoAnything() {
+ @DisableFlags({Flags.FLAG_RECOVERABILITY_DETECTION,
+ Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS})
+ public void testNonPersistentApp() {
// this is old test where the flag needs to be disabled
- mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
-
noteAppCrash(1, false);
noteAppCrash(2, false);
noteAppCrash(3, false);
@@ -371,8 +414,9 @@
}
@Test
+ @EnableFlags(Flags.FLAG_RECOVERABILITY_DETECTION)
+ @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
public void testNonPersistentAppOnlyPerformsFlagResetsRecoverabilityDetection() {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
RescueParty.onSettingsProviderPublished(mMockContext);
verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver),
any(Executor.class),
@@ -408,60 +452,6 @@
}
@Test
- public void testNonPersistentAppCrashDetectionWithScopedResets() {
- // this is old test where the flag needs to be disabled
- mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
-
- RescueParty.onSettingsProviderPublished(mMockContext);
- verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver),
- any(Executor.class),
- mMonitorCallbackCaptor.capture()));
-
- // Record DeviceConfig accesses
- RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
- DeviceConfig.MonitorCallback monitorCallback = mMonitorCallbackCaptor.getValue();
- monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE1);
- monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE2);
- monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE2, NAMESPACE2);
- monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE2, NAMESPACE3);
-
- // Fake DeviceConfig value changes
- monitorCallback.onNamespaceUpdate(NAMESPACE1);
- verify(mMockPackageWatchdog).startObservingHealth(observer,
- Arrays.asList(CALLING_PACKAGE1), RescueParty.DEFAULT_OBSERVING_DURATION_MS);
- monitorCallback.onNamespaceUpdate(NAMESPACE2);
- verify(mMockPackageWatchdog, times(2)).startObservingHealth(eq(observer),
- mPackageListCaptor.capture(),
- eq(RescueParty.DEFAULT_OBSERVING_DURATION_MS));
- monitorCallback.onNamespaceUpdate(NAMESPACE3);
- verify(mMockPackageWatchdog).startObservingHealth(observer,
- Arrays.asList(CALLING_PACKAGE2), RescueParty.DEFAULT_OBSERVING_DURATION_MS);
- assertTrue(mPackageListCaptor.getValue().containsAll(
- Arrays.asList(CALLING_PACKAGE1, CALLING_PACKAGE2)));
- // Perform and verify scoped resets
- final String[] expectedResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2};
- final String[] expectedAllResetNamespaces =
- new String[]{NAMESPACE1, NAMESPACE2, NAMESPACE3};
- HashMap<String, Integer> verifiedTimesMap = new HashMap<String, Integer>();
- observer.execute(new VersionedPackage(
- CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
-
- observer.execute(new VersionedPackage(
- CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 2);
-
- observer.execute(new VersionedPackage(
- CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 3);
-
- observer.execute(new VersionedPackage(
- CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 4);
- assertFalse(RescueParty.isRebootPropertySet());
-
- observer.execute(new VersionedPackage(
- CALLING_PACKAGE1, 1), PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 5);
- assertFalse(RescueParty.isFactoryResetPropertySet());
- }
-
- @Test
public void testIsRecoveryTriggeredReboot() {
for (int i = 0; i < LEVEL_FACTORY_RESET; i++) {
noteBoot(i + 1);
@@ -474,19 +464,6 @@
}
@Test
- public void testIsRecoveryTriggeredRebootRecoverabilityDetection() {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
- for (int i = 0; i < RESCUE_LEVEL_FACTORY_RESET; i++) {
- noteBoot(i + 1);
- }
- assertFalse(RescueParty.isFactoryResetPropertySet());
- setCrashRecoveryPropAttemptingReboot(false);
- noteBoot(RESCUE_LEVEL_FACTORY_RESET + 1);
- assertTrue(RescueParty.isRecoveryTriggeredReboot());
- assertTrue(RescueParty.isFactoryResetPropertySet());
- }
-
- @Test
public void testIsRecoveryTriggeredRebootOnlyAfterRebootCompleted() {
for (int i = 0; i < LEVEL_FACTORY_RESET; i++) {
noteBoot(i + 1);
@@ -505,25 +482,6 @@
}
@Test
- public void testIsRecoveryTriggeredRebootOnlyAfterRebootCompletedRecoverabilityDetection() {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
- for (int i = 0; i < RESCUE_LEVEL_FACTORY_RESET; i++) {
- noteBoot(i + 1);
- }
- int mitigationCount = RESCUE_LEVEL_FACTORY_RESET + 1;
- assertFalse(RescueParty.isFactoryResetPropertySet());
- noteBoot(mitigationCount++);
- assertFalse(RescueParty.isFactoryResetPropertySet());
- noteBoot(mitigationCount++);
- assertFalse(RescueParty.isFactoryResetPropertySet());
- noteBoot(mitigationCount++);
- setCrashRecoveryPropAttemptingReboot(false);
- noteBoot(mitigationCount + 1);
- assertTrue(RescueParty.isRecoveryTriggeredReboot());
- assertTrue(RescueParty.isFactoryResetPropertySet());
- }
-
- @Test
public void testThrottlingOnBootFailures() {
setCrashRecoveryPropAttemptingReboot(false);
long now = System.currentTimeMillis();
@@ -537,20 +495,6 @@
}
@Test
- public void testThrottlingOnBootFailuresRecoverabilityDetection() {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
- setCrashRecoveryPropAttemptingReboot(false);
- long now = System.currentTimeMillis();
- long beforeTimeout = now - TimeUnit.MINUTES.toMillis(
- DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN - 1);
- setCrashRecoveryPropLastFactoryReset(beforeTimeout);
- for (int i = 1; i <= RESCUE_LEVEL_FACTORY_RESET; i++) {
- noteBoot(i);
- }
- assertFalse(RescueParty.isRecoveryTriggeredReboot());
- }
-
- @Test
public void testThrottlingOnAppCrash() {
setCrashRecoveryPropAttemptingReboot(false);
long now = System.currentTimeMillis();
@@ -564,20 +508,6 @@
}
@Test
- public void testThrottlingOnAppCrashRecoverabilityDetection() {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
- setCrashRecoveryPropAttemptingReboot(false);
- long now = System.currentTimeMillis();
- long beforeTimeout = now - TimeUnit.MINUTES.toMillis(
- DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN - 1);
- setCrashRecoveryPropLastFactoryReset(beforeTimeout);
- for (int i = 0; i <= RESCUE_LEVEL_FACTORY_RESET; i++) {
- noteAppCrash(i + 1, true);
- }
- assertFalse(RescueParty.isRecoveryTriggeredReboot());
- }
-
- @Test
public void testNotThrottlingAfterTimeoutOnBootFailures() {
setCrashRecoveryPropAttemptingReboot(false);
long now = System.currentTimeMillis();
@@ -591,20 +521,6 @@
}
@Test
- public void testNotThrottlingAfterTimeoutOnBootFailuresRecoverabilityDetection() {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
- setCrashRecoveryPropAttemptingReboot(false);
- long now = System.currentTimeMillis();
- long afterTimeout = now - TimeUnit.MINUTES.toMillis(
- DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN + 1);
- setCrashRecoveryPropLastFactoryReset(afterTimeout);
- for (int i = 1; i <= RESCUE_LEVEL_FACTORY_RESET; i++) {
- noteBoot(i);
- }
- assertTrue(RescueParty.isRecoveryTriggeredReboot());
- }
-
- @Test
public void testNotThrottlingAfterTimeoutOnAppCrash() {
setCrashRecoveryPropAttemptingReboot(false);
long now = System.currentTimeMillis();
@@ -618,20 +534,7 @@
}
@Test
- public void testNotThrottlingAfterTimeoutOnAppCrashRecoverabilityDetection() {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
- setCrashRecoveryPropAttemptingReboot(false);
- long now = System.currentTimeMillis();
- long afterTimeout = now - TimeUnit.MINUTES.toMillis(
- DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN + 1);
- setCrashRecoveryPropLastFactoryReset(afterTimeout);
- for (int i = 0; i <= RESCUE_LEVEL_FACTORY_RESET; i++) {
- noteAppCrash(i + 1, true);
- }
- assertTrue(RescueParty.isRecoveryTriggeredReboot());
- }
-
- @Test
+ @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
public void testNativeRescuePartyResets() {
doReturn(true).when(() -> SettingsToPropertiesMapper.isNativeFlagsResetPerformed());
doReturn(FAKE_RESET_NATIVE_NAMESPACES).when(
@@ -647,7 +550,6 @@
@Test
public void testExplicitlyEnablingAndDisablingRescue() {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false));
SystemProperties.set(PROP_DISABLE_RESCUE, Boolean.toString(true));
assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage,
@@ -660,7 +562,6 @@
@Test
public void testDisablingRescueByDeviceConfigFlag() {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false));
SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(true));
@@ -686,24 +587,10 @@
}
@Test
- public void testDisablingFactoryResetByDeviceConfigFlagRecoverabilityDetection() {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
- SystemProperties.set(PROP_DISABLE_FACTORY_RESET_FLAG, Boolean.toString(true));
-
- for (int i = 0; i < RESCUE_LEVEL_FACTORY_RESET; i++) {
- noteBoot(i + 1);
- }
- assertFalse(RescueParty.isFactoryResetPropertySet());
-
- // Restore the property value initialized in SetUp()
- SystemProperties.set(PROP_DISABLE_FACTORY_RESET_FLAG, "");
- }
-
- @Test
+ @DisableFlags({Flags.FLAG_RECOVERABILITY_DETECTION,
+ Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS})
public void testHealthCheckLevels() {
// this is old test where the flag needs to be disabled
- mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
-
RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
// Ensure that no action is taken for cases where the failure reason is unknown
@@ -729,8 +616,9 @@
}
@Test
+ @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
+ @EnableFlags(Flags.FLAG_RECOVERABILITY_DETECTION)
public void testHealthCheckLevelsRecoverabilityDetection() {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
// Ensure that no action is taken for cases where the failure reason is unknown
@@ -767,11 +655,31 @@
PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 7),
PackageHealthObserverImpact.USER_IMPACT_LEVEL_40);
}
-
@Test
+ @EnableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
+ public void testHealthCheckLevelsNoFlags() {
+ // this is old test where the flag needs to be disabled
+ RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
+
+ // Ensure that no action is taken for cases where the failure reason is unknown
+ assertEquals(observer.onHealthCheckFailed(null, PackageWatchdog.FAILURE_REASON_UNKNOWN, 1),
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_0);
+
+ // Ensure the correct user impact is returned for each mitigation count.
+ assertEquals(observer.onHealthCheckFailed(null,
+ PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1),
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_50);
+
+ assertEquals(observer.onHealthCheckFailed(null,
+ PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 2),
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
+ }
+ @Test
+ @DisableFlags({Flags.FLAG_RECOVERABILITY_DETECTION,
+ Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS})
public void testBootLoopLevels() {
// this is old test where the flag needs to be disabled
- mSetFlagsRule.disableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
+
RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
@@ -784,8 +692,9 @@
}
@Test
+ @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
+ @EnableFlags(Flags.FLAG_RECOVERABILITY_DETECTION)
public void testBootLoopLevelsRecoverabilityDetection() {
- mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
assertEquals(observer.onBootLoop(1), PackageHealthObserverImpact.USER_IMPACT_LEVEL_40);
@@ -797,6 +706,16 @@
}
@Test
+ @EnableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
+ public void testBootLoopLevelsNoFlags() {
+ RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
+
+ assertEquals(observer.onBootLoop(1), PackageHealthObserverImpact.USER_IMPACT_LEVEL_50);
+ assertEquals(observer.onBootLoop(2), PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
public void testResetDeviceConfigForPackagesOnlyRuntimeMap() {
RescueParty.onSettingsProviderPublished(mMockContext);
verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver),
@@ -827,6 +746,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
public void testResetDeviceConfigForPackagesOnlyPresetMap() {
RescueParty.onSettingsProviderPublished(mMockContext);
verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver),
@@ -835,7 +755,7 @@
String presetMapping = NAMESPACE1 + ":" + CALLING_PACKAGE1 + ","
+ NAMESPACE2 + ":" + CALLING_PACKAGE2 + ","
- + NAMESPACE3 + ":" + CALLING_PACKAGE1;
+ + NAMESPACE3 + ":" + CALLING_PACKAGE1;
doReturn(presetMapping).when(() -> DeviceConfig.getString(
eq(RescueParty.NAMESPACE_CONFIGURATION),
eq(RescueParty.NAMESPACE_TO_PACKAGE_MAPPING_FLAG),
@@ -848,6 +768,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
public void testResetDeviceConfigForPackagesBothMaps() {
RescueParty.onSettingsProviderPublished(mMockContext);
verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver),
@@ -884,6 +805,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
public void testResetDeviceConfigNoExceptionWhenFlagMalformed() {
RescueParty.onSettingsProviderPublished(mMockContext);
verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver),
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
index ecd799f..6ec888c 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
@@ -406,8 +406,6 @@
mContextInjector.getValue().accept(opContext);
verify(mHal).onContextChanged(same(opContext));
- verify(mHal, times(2)).setIgnoreDisplayTouches(
- opContext.operationState.getFingerprintOperationState().isHardwareIgnoringTouches);
client.stopHalOperation();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index 21364b8..87b52e6 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -2068,7 +2068,6 @@
assertThat(mPowerManager.isInteractive()).isTrue();
}
-
@Test
public void handleStandby_fromNonActiveSource_previousActiveSourceNotSet_Standby() {
HdmiCecMessage standbyMessage = HdmiCecMessageBuilder.buildStandby(ADDR_PLAYBACK_1,
@@ -2091,6 +2090,35 @@
.isFalse();
}
+ @Test
+ public void handleReportPhysicalAddress_DeviceDiscoveryActionInProgress_noNewDeviceAction() {
+ mHdmiControlService.getHdmiCecNetwork().clearDeviceList();
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mNativeWrapper.clearResultMessages();
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage reportPhysicalAddressFromPlayback1 =
+ HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+ ADDR_PLAYBACK_1, 0x1000, HdmiDeviceInfo.DEVICE_PLAYBACK);
+ HdmiCecMessage reportPhysicalAddressFromPlayback2 =
+ HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+ ADDR_PLAYBACK_2, 0x2000, HdmiDeviceInfo.DEVICE_PLAYBACK);
+ HdmiCecMessage giveOsdName = HdmiCecMessageBuilder.buildGiveOsdNameCommand(
+ ADDR_TV, ADDR_PLAYBACK_2);
+ // Skip state waiting for <Report Physical Address> for DeviceDiscoveryAction s.t. message
+ // can be dispatched to local device TV.
+ mNativeWrapper.onCecMessage(reportPhysicalAddressFromPlayback1);
+ mNativeWrapper.clearResultMessages();
+ mTestLooper.dispatchAll();
+
+ mNativeWrapper.onCecMessage(reportPhysicalAddressFromPlayback2);
+ mTestLooper.dispatchAll();
+
+ // NewDeviceAction did not start and <Give OSD Name> was not sent.
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveOsdName);
+ }
+
protected static class MockTvDevice extends HdmiCecLocalDeviceTv {
MockTvDevice(HdmiControlService service) {
super(service);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index f5ab95c..f7340ab 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -18,8 +18,10 @@
import static android.app.AutomaticZenRule.TYPE_BEDTIME;
import static android.app.Flags.FLAG_MODES_UI;
+import static android.app.Flags.modesUi;
import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
import static android.provider.Settings.Global.ZEN_MODE_OFF;
+import static android.service.notification.Condition.SOURCE_UNKNOWN;
import static android.service.notification.Condition.SOURCE_USER_ACTION;
import static android.service.notification.Condition.STATE_FALSE;
import static android.service.notification.Condition.STATE_TRUE;
@@ -36,10 +38,18 @@
import static junit.framework.TestCase.assertNull;
import static junit.framework.TestCase.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import android.app.AutomaticZenRule;
import android.app.Flags;
import android.app.NotificationManager.Policy;
import android.content.ComponentName;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Parcel;
import android.platform.test.annotations.DisableFlags;
@@ -66,6 +76,8 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import org.xmlpull.v1.XmlPullParserException;
import java.io.BufferedInputStream;
@@ -102,6 +114,9 @@
private final boolean ENABLED = true;
private final int CREATION_TIME = 123;
+ @Mock
+ PackageManager mPm;
+
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(
SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT);
@@ -119,6 +134,8 @@
@Before
public final void setUp() {
mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+ MockitoAnnotations.initMocks(this);
+ mContext.setMockPackageManager(mPm);
}
@Test
@@ -967,6 +984,85 @@
assertThat(fromXml.zenPolicy).isEqualTo(config.getZenPolicy());
}
+ @Test
+ public void testGetDescription_off() {
+ ZenModeConfig config = new ZenModeConfig();
+ if (!modesUi()) {
+ config.manualRule = new ZenModeConfig.ZenRule();
+ }
+ config.manualRule.pkg = "android";
+ assertThat(ZenModeConfig.getDescription(mContext, true, config, false)).isNull();
+ }
+
+ @Test
+ public void testGetDescription_on_manual_endTime() {
+ ZenModeConfig config = new ZenModeConfig();
+ if (!modesUi()) {
+ config.manualRule = new ZenModeConfig.ZenRule();
+ }
+ config.manualRule.conditionId = ZenModeConfig.toCountdownConditionId(
+ System.currentTimeMillis() + 10000, false);
+ config.manualRule.pkg = "android";
+ config.manualRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ config.manualRule.condition = new Condition(Uri.EMPTY, "", STATE_TRUE, SOURCE_UNKNOWN);
+ assertThat(ZenModeConfig.getDescription(mContext, true, config, false))
+ .startsWith("Until");
+ }
+
+ @Test
+ public void getSoundSummary_on_manual_noEnd() {
+ ZenModeConfig config = new ZenModeConfig();
+ if (!modesUi()) {
+ config.manualRule = new ZenModeConfig.ZenRule();
+ }
+ config.manualRule.conditionId = Uri.EMPTY;
+ config.manualRule.pkg = "android";
+ config.manualRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ config.manualRule.condition = new Condition(Uri.EMPTY, "", STATE_TRUE, SOURCE_UNKNOWN);
+ assertThat(ZenModeConfig.getDescription(mContext, true, config, false)).isNull();
+ }
+
+ @Test
+ public void getSoundSummary_on_manual_enabler() throws Exception {
+ ApplicationInfo ai = mock(ApplicationInfo.class);
+ when(ai.loadLabel(any())).thenReturn("app name");
+ when(mPm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai);
+
+ ZenModeConfig config = new ZenModeConfig();
+ if (!modesUi()) {
+ config.manualRule = new ZenModeConfig.ZenRule();
+ }
+ config.manualRule.conditionId = Uri.EMPTY;
+ config.manualRule.pkg = "android";
+ config.manualRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ config.manualRule.enabler = "app";
+ config.manualRule.condition = new Condition(Uri.EMPTY, "", STATE_TRUE, SOURCE_UNKNOWN);
+ assertThat(ZenModeConfig.getDescription(mContext, true, config, false))
+ .isEqualTo("app name");
+ }
+
+ @Test
+ public void testGetDescription_on_automatic() {
+ ZenModeConfig config = new ZenModeConfig();
+ ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
+ rule.configurationActivity = new ComponentName("a", "a");
+ rule.component = new ComponentName("b", "b");
+ rule.conditionId = new Uri.Builder().scheme("hello").build();
+ rule.condition = new Condition(rule.conditionId, "", Condition.STATE_TRUE);
+ rule.enabled = true;
+ rule.creationTime = 123;
+ rule.id = "id";
+ rule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ rule.modified = true;
+ rule.name = "name";
+ rule.snoozing = false;
+ rule.pkg = "b";
+ config.automaticRules.put("key", rule);
+
+ assertThat(ZenModeConfig.getDescription(mContext, true, config, false))
+ .isEqualTo("name");
+ }
+
private ZenModeConfig getMutedRingerConfig() {
ZenModeConfig config = new ZenModeConfig();
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
index 88a9483..60d8964 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -605,7 +605,7 @@
public void shouldIgnoreVibration_withKeyboardSettingsOff_shouldIgnoreKeyboardVibration() {
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_MEDIUM);
setUserSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, 0 /* OFF*/);
- setHasFixedKeyboardAmplitudeIntensity(true);
+ setKeyboardVibrationSettingsSupported(true);
// Keyboard touch ignored.
assertVibrationIgnoredForAttributes(
@@ -630,7 +630,7 @@
public void shouldIgnoreVibration_withKeyboardSettingsOn_shouldNotIgnoreKeyboardVibration() {
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
setUserSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, 1 /* ON */);
- setHasFixedKeyboardAmplitudeIntensity(true);
+ setKeyboardVibrationSettingsSupported(true);
// General touch ignored.
assertVibrationIgnoredForUsage(USAGE_TOUCH, Vibration.Status.IGNORED_FOR_SETTINGS);
@@ -645,10 +645,10 @@
@Test
@RequiresFlagsEnabled(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED)
- public void shouldIgnoreVibration_noFixedKeyboardAmplitude_ignoresKeyboardTouchVibration() {
+ public void shouldIgnoreVibration_notSupportKeyboardVibration_ignoresKeyboardTouchVibration() {
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
setUserSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, 1 /* ON */);
- setHasFixedKeyboardAmplitudeIntensity(false);
+ setKeyboardVibrationSettingsSupported(false);
// General touch ignored.
assertVibrationIgnoredForUsage(USAGE_TOUCH, Vibration.Status.IGNORED_FOR_SETTINGS);
@@ -974,8 +974,8 @@
when(mVibrationConfigMock.ignoreVibrationsOnWirelessCharger()).thenReturn(ignore);
}
- private void setHasFixedKeyboardAmplitudeIntensity(boolean hasFixedAmplitude) {
- when(mVibrationConfigMock.hasFixedKeyboardAmplitude()).thenReturn(hasFixedAmplitude);
+ private void setKeyboardVibrationSettingsSupported(boolean supported) {
+ when(mVibrationConfigMock.isKeyboardVibrationSettingsSupported()).thenReturn(supported);
}
private void deleteUserSetting(String settingName) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java
index 5125594..36861fa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java
@@ -16,6 +16,15 @@
package com.android.server.wm;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN;
+import static android.hardware.devicestate.feature.flags.Flags.FLAG_DEVICE_STATE_PROPERTY_MIGRATION;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
@@ -24,8 +33,11 @@
import android.content.Context;
import android.content.res.Resources;
+import android.hardware.devicestate.DeviceState;
import android.hardware.devicestate.DeviceStateManager;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
import android.util.Pair;
import androidx.test.filters.SmallTest;
@@ -37,6 +49,8 @@
import org.junit.Before;
import org.junit.Test;
+import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -79,39 +93,67 @@
@Test
public void testInitialization() {
initialize(true /* supportFold */, true /* supportHalfFolded */);
- mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates[0]);
+ mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates.get(0).getIdentifier());
assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState);
}
@Test
public void testInitializationWithNoFoldSupport() {
initialize(false /* supportFold */, false /* supportHalfFolded */);
- mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates[0]);
+ mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates.get(0).getIdentifier());
// Note that the folded state is ignored.
assertEquals(DeviceStateController.DeviceState.UNKNOWN, mCurrentState);
}
@Test
- public void testWithFoldSupported() {
+ @RequiresFlagsDisabled(FLAG_DEVICE_STATE_PROPERTY_MIGRATION)
+ public void testWithFoldSupported_withOverlayConfigValues() {
initialize(true /* supportFold */, false /* supportHalfFolded */);
- mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates[0]);
+ mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates.get(0).getIdentifier());
assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState);
- mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates[0]);
+ mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates.get(0).getIdentifier());
assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState);
- mTarget.onDeviceStateReceivedByDisplayManager(mHalfFoldedStates[0]);
+ mTarget.onDeviceStateReceivedByDisplayManager(mHalfFoldedStates.get(0).getIdentifier());
assertEquals(DeviceStateController.DeviceState.UNKNOWN, mCurrentState); // Ignored
}
@Test
- public void testWithHalfFoldSupported() {
- initialize(true /* supportFold */, true /* supportHalfFolded */);
- mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates[0]);
+ @RequiresFlagsEnabled(FLAG_DEVICE_STATE_PROPERTY_MIGRATION)
+ public void testWithFoldSupported_withDeviceStateManagerPropertyAPI() {
+ initialize(true /* supportFold */, false /* supportHalfFolded */);
+ mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates.get(0).getIdentifier());
assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState);
- mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates[0]);
+ mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates.get(0).getIdentifier());
assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState);
- mTarget.onDeviceStateReceivedByDisplayManager(mHalfFoldedStates[0]);
+ mTarget.onDeviceStateReceivedByDisplayManager(mHalfFoldedStates.get(0).getIdentifier());
+ assertEquals(DeviceStateController.DeviceState.UNKNOWN, mCurrentState); // Ignored
+ }
+
+ @Test
+ @RequiresFlagsDisabled(FLAG_DEVICE_STATE_PROPERTY_MIGRATION)
+ public void testWithHalfFoldSupported_withOverlayConfigValue() {
+ initialize(true /* supportFold */, true /* supportHalfFolded */);
+ mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates.get(0).getIdentifier());
+ assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState);
+ mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates.get(0).getIdentifier());
+ assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState);
+ mTarget.onDeviceStateReceivedByDisplayManager(mHalfFoldedStates.get(0).getIdentifier());
assertEquals(DeviceStateController.DeviceState.HALF_FOLDED, mCurrentState);
- mTarget.onDeviceStateReceivedByDisplayManager(mConcurrentDisplayState);
+ mTarget.onDeviceStateReceivedByDisplayManager(mConcurrentDisplayState.getIdentifier());
+ assertEquals(DeviceStateController.DeviceState.CONCURRENT, mCurrentState);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_DEVICE_STATE_PROPERTY_MIGRATION)
+ public void testWithHalfFoldSupported_withDeviceStateManagerPropertyApi() {
+ initialize(true /* supportFold */, true /* supportHalfFolded */);
+ mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates.get(0).getIdentifier());
+ assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState);
+ mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates.get(0).getIdentifier());
+ assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState);
+ mTarget.onDeviceStateReceivedByDisplayManager(mHalfFoldedStates.get(0).getIdentifier());
+ assertEquals(DeviceStateController.DeviceState.HALF_FOLDED, mCurrentState);
+ mTarget.onDeviceStateReceivedByDisplayManager(mConcurrentDisplayState.getIdentifier());
assertEquals(DeviceStateController.DeviceState.CONCURRENT, mCurrentState);
}
@@ -121,16 +163,18 @@
assertEquals(1, mTarget.mDeviceStateCallbacks.size());
assertTrue(mTarget.mDeviceStateCallbacks.containsKey(mDelegate));
- mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates[0]);
+ mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates.get(0).getIdentifier());
assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState);
- mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates[0]);
+ mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates.get(0).getIdentifier());
assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState);
// The callback should not receive state change when it is unregistered.
mTarget.unregisterDeviceStateCallback(mDelegate);
assertTrue(mTarget.mDeviceStateCallbacks.isEmpty());
- mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates[0]);
- assertEquals(DeviceStateController.DeviceState.FOLDED /* unchanged */, mCurrentState);
+
+ mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates.get(0).getIdentifier());
+ assertEquals(DeviceStateController.DeviceState.FOLDED /* unchanged */,
+ mCurrentState);
}
@Test
@@ -151,16 +195,50 @@
assertEquals(mExecutor, entries.get(0).second);
}
- private final int[] mFoldedStates = {0};
- private final int[] mOpenDeviceStates = {1};
- private final int[] mHalfFoldedStates = {2};
- private final int[] mRearDisplayStates = {3};
- private final int mConcurrentDisplayState = 4;
+ private final List<DeviceState> mFoldedStates = new ArrayList<>(
+ List.of(new DeviceState(new DeviceState.Configuration.Builder(0,
+ "folded").setSystemProperties(new HashSet<>(
+ List.of(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY)))
+ .setPhysicalProperties(new HashSet<>(
+ List.of(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED)))
+ .build())));
+ private final List<DeviceState> mOpenDeviceStates = new ArrayList<>(
+ List.of(new DeviceState(new DeviceState.Configuration.Builder(1,
+ "open").setSystemProperties(new HashSet<>(
+ List.of(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY)))
+ .setPhysicalProperties(new HashSet<>(
+ List.of(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN)))
+ .build())));
+ private final List<DeviceState> mHalfFoldedStates = new ArrayList<>(
+ List.of(new DeviceState(new DeviceState.Configuration.Builder(2,
+ "half_folded").setSystemProperties(new HashSet<>(
+ List.of(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY)))
+ .setPhysicalProperties(new HashSet<>(
+ List.of(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN)))
+ .build())));
+ private final List<DeviceState> mRearDisplayStates = new ArrayList<>(
+ List.of(new DeviceState(new DeviceState.Configuration.Builder(3,
+ "rear_display").setSystemProperties(new HashSet<>(
+ List.of(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY,
+ PROPERTY_FEATURE_REAR_DISPLAY)))
+ .setPhysicalProperties(new HashSet<>(
+ List.of(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN)))
+ .build())));
+ private final DeviceState mConcurrentDisplayState = new DeviceState(
+ new DeviceState.Configuration.Builder(4, "concurrent_display")
+ .setSystemProperties(new HashSet<>(List.of(
+ PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY,
+ PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT)))
+ .setPhysicalProperties(new HashSet<>(List.of(
+ PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN)))
+ .build());
private class DeviceStateControllerBuilder {
private boolean mSupportFold = false;
private boolean mSupportHalfFold = false;
+
private Consumer<DeviceStateController.DeviceState> mDelegate;
+ private final List<DeviceState> mDeviceStateList = new ArrayList<>();
DeviceStateControllerBuilder setSupportFold(
boolean supportFold, boolean supportHalfFold) {
@@ -179,13 +257,17 @@
if (enableFold || enableHalfFold) {
when(mMockContext.getResources()
.getIntArray(R.array.config_openDeviceStates))
- .thenReturn(mOpenDeviceStates);
+ .thenReturn(mapDeviceStateListToIdentifierArray(mOpenDeviceStates));
when(mMockContext.getResources()
.getIntArray(R.array.config_rearDisplayDeviceStates))
- .thenReturn(mRearDisplayStates);
+ .thenReturn(mapDeviceStateListToIdentifierArray(mRearDisplayStates));
when(mMockContext.getResources()
.getInteger(R.integer.config_deviceStateConcurrentRearDisplay))
- .thenReturn(mConcurrentDisplayState);
+ .thenReturn(mConcurrentDisplayState.getIdentifier());
+
+ mDeviceStateList.addAll(mOpenDeviceStates);
+ mDeviceStateList.addAll(mRearDisplayStates);
+ mDeviceStateList.add(mConcurrentDisplayState);
} else {
// Match the default value in framework resources
when(mMockContext.getResources()
@@ -196,12 +278,14 @@
if (enableFold) {
when(mMockContext.getResources()
.getIntArray(R.array.config_foldedDeviceStates))
- .thenReturn(mFoldedStates);
+ .thenReturn(mapDeviceStateListToIdentifierArray(mFoldedStates));
+ mDeviceStateList.addAll(mFoldedStates);
}
if (enableHalfFold) {
when(mMockContext.getResources()
.getIntArray(R.array.config_halfFoldedDeviceStates))
- .thenReturn(mHalfFoldedStates);
+ .thenReturn(mapDeviceStateListToIdentifierArray(mHalfFoldedStates));
+ mDeviceStateList.addAll(mHalfFoldedStates);
}
}
@@ -210,11 +294,20 @@
mMockDeviceStateManager = mock(DeviceStateManager.class);
when(mMockContext.getSystemService(DeviceStateManager.class))
.thenReturn(mMockDeviceStateManager);
+ when(mMockDeviceStateManager.getSupportedDeviceStates()).thenReturn(mDeviceStateList);
Resources mockRes = mock(Resources.class);
when(mMockContext.getResources()).thenReturn((mockRes));
mockFold(mSupportFold, mSupportHalfFold);
mTarget = new DeviceStateController(mMockContext, new WindowManagerGlobalLock());
mTarget.registerDeviceStateCallback(mDelegate, mExecutor);
}
+
+ private int[] mapDeviceStateListToIdentifierArray(List<DeviceState> deviceStates) {
+ int[] identifiers = new int[deviceStates.size()];
+ for (int i = 0; i < deviceStates.size(); i++) {
+ identifiers[i] = deviceStates.get(i).getIdentifier();
+ }
+ return identifiers;
+ }
}
}
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 0ddc38a..7ed26fb 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -35,6 +35,7 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.flags.Flags;
import java.util.HashMap;
import java.util.HashSet;
@@ -716,14 +717,15 @@
}
private static int getCarrierPrivilegeStatus(Context context, int subId, int uid) {
- if (uid == Process.SYSTEM_UID || uid == Process.PHONE_UID) {
+ if (isSystemOrPhone(uid)) {
// Skip the check if it's one of these special uids
return TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
}
+
final long identity = Binder.clearCallingIdentity();
try {
TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(
- Context.TELEPHONY_SERVICE);
+ Context.TELEPHONY_SERVICE);
return telephonyManager.createForSubscriptionId(subId).getCarrierPrivilegeStatus(uid);
} finally {
Binder.restoreCallingIdentity(identity);
@@ -926,4 +928,30 @@
|| checkCallingOrSelfReadPhoneNumber(context, subId, callingPackage,
callingFeatureId, message));
}
+
+ /**
+ * @return true if the specified {@code uid} is for a system or phone process, no matter if runs
+ * as system user or not.
+ */
+ public static boolean isSystemOrPhone(int uid) {
+ if (Flags.supportPhoneUidCheckForMultiuser()) {
+ return UserHandle.isSameApp(uid, Process.SYSTEM_UID) || UserHandle.isSameApp(uid,
+ Process.PHONE_UID);
+ } else {
+ return uid == Process.SYSTEM_UID || uid == Process.PHONE_UID;
+ }
+ }
+
+ /**
+ * @return true if the specified {@code uid} is for a ROOT or SHELL process, no matter if runs
+ * as system user or not.
+ */
+ public static boolean isRootOrShell(int uid) {
+ if (Flags.supportPhoneUidCheckForMultiuser()) {
+ return UserHandle.isSameApp(uid, Process.ROOT_UID) || UserHandle.isSameApp(uid,
+ Process.SHELL_UID);
+ } else {
+ return uid == Process.ROOT_UID || uid == Process.SHELL_UID;
+ }
+ }
}
diff --git a/tests/Input/assets/testPointerStrokeStyle.png b/tests/Input/assets/testPointerStrokeStyle.png
new file mode 100644
index 0000000..4ddde70
--- /dev/null
+++ b/tests/Input/assets/testPointerStrokeStyle.png
Binary files differ
diff --git a/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt b/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt
index d196b85..e0f8c6d 100644
--- a/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt
+++ b/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt
@@ -88,6 +88,35 @@
theme.applyStyle(
PointerIcon.vectorFillStyleToResource(PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_GREEN),
/* force= */ true)
+ theme.applyStyle(PointerIcon.vectorStrokeStyleToResource(
+ PointerIcon.POINTER_ICON_VECTOR_STYLE_STROKE_WHITE), /* force= */ true)
+
+ val pointerIcon =
+ PointerIcon.getLoadedSystemIcon(
+ ContextThemeWrapper(context, theme),
+ PointerIcon.TYPE_ARROW,
+ /* useLargeIcons= */ false,
+ /* pointerScale= */ 1f)
+
+ pointerIcon.getBitmap().assertAgainstGolden(
+ screenshotRule,
+ testName.methodName,
+ exactScreenshotMatcher
+ )
+ }
+
+ @Test
+ fun testPointerStrokeStyle() {
+ assumeTrue(enableVectorCursors())
+ assumeTrue(enableVectorCursorA11ySettings())
+
+ val theme: Resources.Theme = context.getResources().newTheme()
+ theme.setTo(context.getTheme())
+ theme.applyStyle(
+ PointerIcon.vectorFillStyleToResource(PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_BLACK),
+ /* force= */ true)
+ theme.applyStyle(PointerIcon.vectorStrokeStyleToResource(
+ PointerIcon.POINTER_ICON_VECTOR_STYLE_STROKE_BLACK), /* force= */ true)
val pointerIcon =
PointerIcon.getLoadedSystemIcon(
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 6a17ef8..df1d51e 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -763,10 +763,35 @@
pool->setTo(chunk, android::util::DeviceToHost32(
(reinterpret_cast<const ResChunk_header*>(chunk))->size));
- printer_->Print("\n");
+ printer_->Print(StringPrintf(" strings: %zd styles %zd flags: %s|%s\n", pool->size(),
+ pool->styleCount(), pool->isUTF8() ? "UTF-8" : "UTF-16",
+ pool->isSorted() ? "SORTED" : "NON-SORTED"));
for (size_t i = 0; i < pool->size(); i++) {
printer_->Print(StringPrintf("#%zd : %s\n", i, android::util::GetString(*pool, i).c_str()));
+ if (i < pool->styleCount()) {
+ printer_->Print(" [Style] ");
+ auto maybe_style = pool->styleAt(i);
+ if (!maybe_style) {
+ printer_->Print("??? missing\n");
+ } else {
+ std::vector<const ResStringPool_span*> spans;
+ for (auto style = maybe_style.value().unsafe_ptr();
+ style->name.index != android::ResStringPool_span::END; ++style) {
+ spans.push_back(style);
+ }
+ printer_->Print(StringPrintf("(%zd)", spans.size()));
+ if (!spans.empty()) {
+ printer_->Print(" :");
+ for (const auto& span : spans) {
+ printer_->Print(StringPrintf(
+ " %s:%u,%u", android::util::GetString(*pool, span->name.index).c_str(),
+ span->firstChar, span->lastChar));
+ }
+ printer_->Print("\n");
+ }
+ }
+ }
}
}
diff --git a/tools/aapt2/ResourceValues_test.cpp b/tools/aapt2/ResourceValues_test.cpp
index d788e3f..b30348d 100644
--- a/tools/aapt2/ResourceValues_test.cpp
+++ b/tools/aapt2/ResourceValues_test.cpp
@@ -184,6 +184,35 @@
EXPECT_THAT(pool_b.strings()[0]->value, StrEq("hello"));
}
+TEST(ResourcesValuesTest, StringEquals) {
+ android::StringPool pool;
+
+ String str(pool.MakeRef("hello", android::StringPool::Context(test::ParseConfigOrDie("en"))));
+ String str2(pool.MakeRef("hello"));
+ EXPECT_TRUE(str.Equals(&str2));
+ EXPECT_TRUE(str2.Equals(&str));
+
+ String str3(pool.MakeRef("how are you"));
+ EXPECT_FALSE(str.Equals(&str3));
+}
+
+TEST(ResourcesValuesTest, StyledStringEquals) {
+ android::StringPool pool;
+
+ StyledString ss(pool.MakeRef(android::StyleString{"hello", {{"b", 0, 1}, {"u", 2, 4}}}));
+ StyledString ss2(pool.MakeRef(android::StyleString{"hello", {{"b", 0, 1}, {"u", 2, 4}}}));
+ StyledString ss3(pool.MakeRef(android::StyleString{"hi", {{"b", 0, 1}, {"u", 2, 4}}}));
+ StyledString ss4(pool.MakeRef(android::StyleString{"hello", {{"b", 0, 1}}}));
+ StyledString ss5(pool.MakeRef(android::StyleString{"hello", {{"b", 0, 1}, {"u", 3, 4}}}));
+ StyledString ss6(pool.MakeRef(android::StyleString{"hello", {{"b", 0, 1}, {"s", 2, 4}}}));
+ EXPECT_TRUE(ss.Equals(&ss2));
+ EXPECT_TRUE(ss2.Equals(&ss));
+ EXPECT_FALSE(ss.Equals(&ss3));
+ EXPECT_FALSE(ss.Equals(&ss4));
+ EXPECT_FALSE(ss.Equals(&ss5));
+ EXPECT_FALSE(ss.Equals(&ss6));
+}
+
TEST(ResourceValuesTest, StyleMerges) {
android::StringPool pool_a;
android::StringPool pool_b;
diff --git a/tools/aapt2/cmd/Diff.cpp b/tools/aapt2/cmd/Diff.cpp
index 5bfc732..6da3176 100644
--- a/tools/aapt2/cmd/Diff.cpp
+++ b/tools/aapt2/cmd/Diff.cpp
@@ -106,7 +106,7 @@
if (!value_a->Equals(value_b)) {
std::stringstream str_stream;
str_stream << "value " << pkg_a.name << ":" << type_a.named_type << "/" << entry_a.name
- << " config=" << config_value_a->config << " does not match:\n";
+ << " config='" << config_value_a->config << "' does not match:\n";
value_a->Print(&str_stream);
str_stream << "\n vs \n";
value_b->Print(&str_stream);
diff --git a/tools/aapt2/test/Fixture.h b/tools/aapt2/test/Fixture.h
index ba4a734..14298d16 100644
--- a/tools/aapt2/test/Fixture.h
+++ b/tools/aapt2/test/Fixture.h
@@ -127,4 +127,4 @@
} // namespace aapt
-#endif // AAPT_TEST_FIXTURE_H
\ No newline at end of file
+#endif // AAPT_TEST_FIXTURE_H