Merge "Reset blur radius to 0 whenever blur is not supported" into main
diff --git a/core/api/current.txt b/core/api/current.txt
index 050cad4..d4ed533 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -2419,6 +2419,7 @@
field public static final int accessibilityActionScrollRight = 16908347; // 0x102003b
field public static final int accessibilityActionScrollToPosition = 16908343; // 0x1020037
field public static final int accessibilityActionScrollUp = 16908344; // 0x1020038
+ field @FlaggedApi("android.view.accessibility.a11y_selection_api") public static final int accessibilityActionSetExtendedSelection;
field public static final int accessibilityActionSetProgress = 16908349; // 0x102003d
field public static final int accessibilityActionShowOnScreen = 16908342; // 0x1020036
field public static final int accessibilityActionShowTextSuggestions = 16908376; // 0x1020058
@@ -56266,6 +56267,7 @@
method public android.view.accessibility.AccessibilityNodeInfo getParent();
method @Nullable public android.view.accessibility.AccessibilityNodeInfo getParent(int);
method public android.view.accessibility.AccessibilityNodeInfo.RangeInfo getRangeInfo();
+ method @FlaggedApi("android.view.accessibility.a11y_selection_api") @Nullable public android.view.accessibility.AccessibilityNodeInfo.Selection getSelection();
method @Nullable public CharSequence getStateDescription();
method @FlaggedApi("android.view.accessibility.supplemental_description") @Nullable public CharSequence getSupplementalDescription();
method public CharSequence getText();
@@ -56374,6 +56376,7 @@
method public void setScreenReaderFocusable(boolean);
method public void setScrollable(boolean);
method public void setSelected(boolean);
+ method @FlaggedApi("android.view.accessibility.a11y_selection_api") public void setSelection(@Nullable android.view.accessibility.AccessibilityNodeInfo.Selection);
method public void setShowingHintText(boolean);
method public void setSource(android.view.View);
method public void setSource(android.view.View, int);
@@ -56406,6 +56409,7 @@
field public static final String ACTION_ARGUMENT_ROW_INT = "android.view.accessibility.action.ARGUMENT_ROW_INT";
field @FlaggedApi("android.view.accessibility.granular_scrolling") public static final String ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT = "android.view.accessibility.action.ARGUMENT_SCROLL_AMOUNT_FLOAT";
field public static final String ACTION_ARGUMENT_SELECTION_END_INT = "ACTION_ARGUMENT_SELECTION_END_INT";
+ field @FlaggedApi("android.view.accessibility.a11y_selection_api") public static final String ACTION_ARGUMENT_SELECTION_PARCELABLE = "android.view.accessibility.action.ARGUMENT_SELECTION_PARCELABLE";
field public static final String ACTION_ARGUMENT_SELECTION_START_INT = "ACTION_ARGUMENT_SELECTION_START_INT";
field public static final String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE = "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
field public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 128; // 0x80
@@ -56503,6 +56507,7 @@
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SCROLL_TO_POSITION;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SCROLL_UP;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SELECT;
+ field @FlaggedApi("android.view.accessibility.a11y_selection_api") @NonNull public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SET_EXTENDED_SELECTION;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SET_PROGRESS;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SET_SELECTION;
field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SET_TEXT;
@@ -56588,6 +56593,26 @@
field public static final int RANGE_TYPE_PERCENT = 2; // 0x2
}
+ @FlaggedApi("android.view.accessibility.a11y_selection_api") public static final class AccessibilityNodeInfo.Selection implements android.os.Parcelable {
+ ctor public AccessibilityNodeInfo.Selection(@NonNull android.view.accessibility.AccessibilityNodeInfo.SelectionPosition, @NonNull android.view.accessibility.AccessibilityNodeInfo.SelectionPosition);
+ method public int describeContents();
+ method @NonNull public android.view.accessibility.AccessibilityNodeInfo.SelectionPosition getEnd();
+ method @NonNull public android.view.accessibility.AccessibilityNodeInfo.SelectionPosition getStart();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.view.accessibility.AccessibilityNodeInfo.Selection> CREATOR;
+ }
+
+ @FlaggedApi("android.view.accessibility.a11y_selection_api") public static final class AccessibilityNodeInfo.SelectionPosition implements android.os.Parcelable {
+ ctor public AccessibilityNodeInfo.SelectionPosition(@NonNull android.view.accessibility.AccessibilityNodeInfo, int);
+ ctor public AccessibilityNodeInfo.SelectionPosition(@NonNull android.view.View, int);
+ ctor public AccessibilityNodeInfo.SelectionPosition(@NonNull android.view.View, int, int);
+ method public int describeContents();
+ method @Nullable public android.view.accessibility.AccessibilityNodeInfo getNode();
+ method public int getOffset();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.view.accessibility.AccessibilityNodeInfo.SelectionPosition> CREATOR;
+ }
+
public static final class AccessibilityNodeInfo.TouchDelegateInfo implements android.os.Parcelable {
ctor public AccessibilityNodeInfo.TouchDelegateInfo(@NonNull java.util.Map<android.graphics.Region,android.view.View>);
method public int describeContents();
diff --git a/core/api/lint-baseline.txt b/core/api/lint-baseline.txt
index e71dffa..577113b 100644
--- a/core/api/lint-baseline.txt
+++ b/core/api/lint-baseline.txt
@@ -1,3 +1,4 @@
+
// Baseline format: 1.0
BroadcastBehavior: android.app.AlarmManager#ACTION_NEXT_ALARM_CLOCK_CHANGED:
Field 'ACTION_NEXT_ALARM_CLOCK_CHANGED' is missing @BroadcastBehavior
@@ -243,8 +244,6 @@
Field 'ACTION_SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED' is missing @BroadcastBehavior
BroadcastBehavior: android.telephony.euicc.EuiccManager#ACTION_NOTIFY_CARRIER_SETUP_INCOMPLETE:
Field 'ACTION_NOTIFY_CARRIER_SETUP_INCOMPLETE' is missing @BroadcastBehavior
-
-
DeprecationMismatch: android.accounts.AccountManager#newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, String[], boolean, String, String, String[], android.os.Bundle):
Method android.accounts.AccountManager.newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, String[], boolean, String, String, String[], android.os.Bundle): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
DeprecationMismatch: android.app.Activity#enterPictureInPictureMode():
@@ -381,8 +380,6 @@
Method android.webkit.WebViewDatabase.hasFormData(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
DeprecationMismatch: javax.microedition.khronos.egl.EGL10#eglCreatePixmapSurface(javax.microedition.khronos.egl.EGLDisplay, javax.microedition.khronos.egl.EGLConfig, Object, int[]):
Method javax.microedition.khronos.egl.EGL10.eglCreatePixmapSurface(javax.microedition.khronos.egl.EGLDisplay, javax.microedition.khronos.egl.EGLConfig, Object, int[]): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
-
-
FlaggedApiLiteral: android.Manifest.permission#BIND_APP_FUNCTION_SERVICE:
@FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER).
FlaggedApiLiteral: android.Manifest.permission#BIND_TV_AD_SERVICE:
@@ -405,26 +402,22 @@
@FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.content.pm.Flags.FLAG_SDK_LIB_INDEPENDENCE).
FlaggedApiLiteral: android.R.attr#supplementalDescription:
@FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.view.accessibility.Flags.FLAG_SUPPLEMENTAL_DESCRIPTION).
+FlaggedApiLiteral: android.R.id#accessibilityActionSetExtendedSelection:
+ @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.view.accessibility.Flags.FLAG_A11Y_SELECTION_API).
FlaggedApiLiteral: android.accessibilityservice.AccessibilityService#OVERLAY_RESULT_INTERNAL_ERROR:
@FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.view.accessibility.Flags.FLAG_A11Y_OVERLAY_CALLBACKS).
FlaggedApiLiteral: android.accessibilityservice.AccessibilityService#OVERLAY_RESULT_INVALID:
@FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.view.accessibility.Flags.FLAG_A11Y_OVERLAY_CALLBACKS).
FlaggedApiLiteral: android.accessibilityservice.AccessibilityService#OVERLAY_RESULT_SUCCESS:
@FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.view.accessibility.Flags.FLAG_A11Y_OVERLAY_CALLBACKS).
-
-
InvalidNullabilityOverride: android.app.Notification.TvExtender#extend(android.app.Notification.Builder) parameter #0:
Invalid nullability on parameter `builder` in method `extend`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
InvalidNullabilityOverride: android.media.midi.MidiUmpDeviceService#onBind(android.content.Intent) parameter #0:
Invalid nullability on parameter `intent` in method `onBind`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
-
-
KotlinOperator: android.graphics.Matrix44#get(int, int):
Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
KotlinOperator: android.graphics.Matrix44#set(int, int, float):
Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
-
-
MissingGetterMatchingBuilder: android.os.RemoteCallbackList.Builder#setInterfaceDiedCallback(android.os.RemoteCallbackList.Builder.InterfaceDiedCallback<E>):
android.os.RemoteCallbackList does not declare a `getInterfaceDiedCallback()` method matching method android.os.RemoteCallbackList.Builder.setInterfaceDiedCallback(android.os.RemoteCallbackList.Builder.InterfaceDiedCallback<E>)
RequiresPermission: android.accounts.AccountManager#getAccountsByTypeAndFeatures(String, String[], android.accounts.AccountManagerCallback<android.accounts.Account[]>, android.os.Handler):
@@ -1117,14 +1110,10 @@
Method 'setBlockNetworkLoads' documentation mentions permissions without declaring @RequiresPermission
RequiresPermission: android.webkit.WebSettings#setGeolocationEnabled(boolean):
Method 'setGeolocationEnabled' documentation mentions permissions without declaring @RequiresPermission
-
-
Todo: android.hardware.camera2.params.StreamConfigurationMap:
Documentation mentions 'TODO'
Todo: android.provider.ContactsContract.RawContacts#newEntityIterator(android.database.Cursor):
Documentation mentions 'TODO'
-
-
UnflaggedApi: android.R.color#on_surface_disabled_material:
New API must be flagged with @FlaggedApi: field android.R.color.on_surface_disabled_material
UnflaggedApi: android.R.color#outline_disabled_material:
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 3633b4e..0ca4a32 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -11204,10 +11204,15 @@
* </pre>
*
*
- *
+ * <p>
* NOTE: The progress bar layout will be mirrored for RTL layout.
+ * </p>
+ *
+ * <p>
* NOTE: The extras set by {@link Notification.Builder#setProgress} will be overridden by
- * the values set on this style object when the notification is built.
+ * the values set on this style object when the notification is built. Therefore, that method
+ * is not used with this style.
+ * </p>
*
*/
@FlaggedApi(Flags.FLAG_API_RICH_ONGOING)
diff --git a/media/java/android/media/Image.java b/core/java/android/media/Image.java
similarity index 100%
rename from media/java/android/media/Image.java
rename to core/java/android/media/Image.java
diff --git a/media/java/android/media/ImageReader.java b/core/java/android/media/ImageReader.java
similarity index 100%
rename from media/java/android/media/ImageReader.java
rename to core/java/android/media/ImageReader.java
diff --git a/media/java/android/media/ImageUtils.java b/core/java/android/media/ImageUtils.java
similarity index 100%
rename from media/java/android/media/ImageUtils.java
rename to core/java/android/media/ImageUtils.java
diff --git a/media/java/android/media/ImageWriter.java b/core/java/android/media/ImageWriter.java
similarity index 100%
rename from media/java/android/media/ImageWriter.java
rename to core/java/android/media/ImageWriter.java
diff --git a/media/java/android/media/PublicFormatUtils.java b/core/java/android/media/PublicFormatUtils.java
similarity index 100%
rename from media/java/android/media/PublicFormatUtils.java
rename to core/java/android/media/PublicFormatUtils.java
diff --git a/core/java/android/view/FrameMetrics.java b/core/java/android/view/FrameMetrics.java
index 58b2a67..a4fc342 100644
--- a/core/java/android/view/FrameMetrics.java
+++ b/core/java/android/view/FrameMetrics.java
@@ -259,19 +259,20 @@
int FRAME_DEADLINE = 9;
int FRAME_START_TIME = 10;
int FRAME_INTERVAL = 11;
- int SYNC_QUEUED = 12;
- int SYNC_START = 13;
- int ISSUE_DRAW_COMMANDS_START = 14;
- int SWAP_BUFFERS = 15;
- int FRAME_COMPLETED = 16;
- int DEQUEUE_BUFFER_DURATION = 17;
- int QUEUE_BUFFER_DURATION = 18;
- int GPU_COMPLETED = 19;
- int SWAP_BUFFERS_COMPLETED = 20;
- int DISPLAY_PRESENT_TIME = 21;
- int COMMAND_SUBMISSION_COMPLETED = 22;
+ int WORKLOAD_TARGET = 12;
+ int SYNC_QUEUED = 13;
+ int SYNC_START = 14;
+ int ISSUE_DRAW_COMMANDS_START = 15;
+ int SWAP_BUFFERS = 16;
+ int FRAME_COMPLETED = 17;
+ int DEQUEUE_BUFFER_DURATION = 18;
+ int QUEUE_BUFFER_DURATION = 19;
+ int GPU_COMPLETED = 20;
+ int SWAP_BUFFERS_COMPLETED = 21;
+ int DISPLAY_PRESENT_TIME = 22;
+ int COMMAND_SUBMISSION_COMPLETED = 23;
- int FRAME_STATS_COUNT = 23; // must always be last and in sync with
+ int FRAME_STATS_COUNT = 24; // must always be last and in sync with
// FrameInfoIndex::NumIndexes in libs/hwui/FrameInfo.h
}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 8a10979..578b7b6 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -541,6 +541,22 @@
"ACTION_ARGUMENT_HTML_ELEMENT_STRING";
/**
+ * Argument for specifying the extended selection.
+ *
+ * <p><strong>Type:</strong> {@link AccessibilityNodeInfo.Selection}<br>
+ * <strong>Actions:</strong>
+ *
+ * <ul>
+ * <li>{@link AccessibilityAction#ACTION_SET_EXTENDED_SELECTION}
+ * </ul>
+ *
+ * @see AccessibilityAction#ACTION_SET_EXTENDED_SELECTION
+ */
+ @FlaggedApi(Flags.FLAG_A11Y_SELECTION_API)
+ public static final String ACTION_ARGUMENT_SELECTION_PARCELABLE =
+ "android.view.accessibility.action.ARGUMENT_SELECTION_PARCELABLE";
+
+ /**
* Argument for whether when moving at granularity to extend the selection
* or to move it otherwise.
* <p>
@@ -1146,6 +1162,8 @@
private int mConnectionId = UNDEFINED_CONNECTION_ID;
+ private Selection mSelection;
+
private RangeInfo mRangeInfo;
private CollectionInfo mCollectionInfo;
private CollectionItemInfo mCollectionItemInfo;
@@ -2660,6 +2678,56 @@
}
/**
+ * Sets the extended selection, which is a representation of selection that spans multiple nodes
+ * that exist within the subtree of the node defining selection.
+ *
+ * <p><b>Note:</b> The start and end {@link SelectionPosition} of the provided {@link Selection}
+ * should be constructed with {@code this} node or a descendant of it.
+ *
+ * <p><b>Note:</b> {@link AccessibilityNodeInfo#setFocusable} and {@link
+ * AccessibilityNodeInfo#setFocused} should both be called with {@code true} before setting the
+ * selection in order to make {@code this} node a candidate to contain a selection.
+ *
+ * <p><b>Note:</b> Cannot be called from an AccessibilityService. This class is made immutable
+ * before being delivered to an AccessibilityService.
+ *
+ * @param selection The extended selection within the node's subtree, or {@code null} if no
+ * selection exists.
+ * @see AccessibilityNodeInfo.AccessibilityAction#ACTION_SET_EXTENDED_SELECTION
+ * @throws IllegalStateException If called from an AccessibilityService
+ */
+ @FlaggedApi(Flags.FLAG_A11Y_SELECTION_API)
+ public void setSelection(@Nullable Selection selection) {
+ enforceNotSealed();
+ mSelection = selection;
+ }
+
+ /**
+ * Gets the extended selection, which is a representation of selection that spans multiple nodes
+ * that exist within the subtree of the node defining selection.
+ *
+ * <p><b>Note:</b> The start and end {@link SelectionPosition} of the provided {@link Selection}
+ * should be constructed with {@code this} node or a descendant of it.
+ *
+ * <p><b>Note:</b> In order for a node to be a candidate to contain a selection, {@link
+ * AccessibilityNodeInfo#isFocusable()} ()} and {@link AccessibilityNodeInfo#isFocused()} should
+ * both be return with {@code true}.
+ *
+ * @return The extended selection within the node's subtree, or {@code null} if no selection
+ * exists.
+ */
+ @FlaggedApi(Flags.FLAG_A11Y_SELECTION_API)
+ public @Nullable Selection getSelection() {
+ if (mSelection != null) {
+ mSelection.getStart().setWindowId(mWindowId);
+ mSelection.getStart().setConnectionId(mConnectionId);
+ mSelection.getEnd().setWindowId(mWindowId);
+ mSelection.getEnd().setConnectionId(mConnectionId);
+ }
+ return mSelection;
+ }
+
+ /**
* Gets whether this node is visible to the user.
* <p>
* Between {@link Build.VERSION_CODES#JELLY_BEAN API 16} and
@@ -4168,6 +4236,15 @@
* there is no text selection and no cursor.
*/
public int getTextSelectionStart() {
+ if (Flags.a11ySelectionApi()) {
+ Selection current = getSelection();
+ if ((current != null)
+ && current.getStart().usesNode(this)
+ && current.getEnd().usesNode(this)) {
+ return current.getStart().getOffset();
+ }
+ return UNDEFINED_SELECTION_INDEX;
+ }
return mTextSelectionStart;
}
@@ -4183,6 +4260,15 @@
* there is no text selection and no cursor.
*/
public int getTextSelectionEnd() {
+ if (Flags.a11ySelectionApi()) {
+ Selection current = getSelection();
+ if ((current != null)
+ && current.getStart().usesNode(this)
+ && current.getEnd().usesNode(this)) {
+ return current.getEnd().getOffset();
+ }
+ return UNDEFINED_SELECTION_INDEX;
+ }
return mTextSelectionEnd;
}
@@ -4201,6 +4287,13 @@
*/
public void setTextSelection(int start, int end) {
enforceNotSealed();
+ if (Flags.a11ySelectionApi()) {
+ Selection selection =
+ new Selection(
+ new SelectionPosition(this, start), new SelectionPosition(this, end));
+ setSelection(selection);
+ return;
+ }
mTextSelectionStart = start;
mTextSelectionEnd = end;
}
@@ -4875,6 +4968,10 @@
nonDefaultFields |= bitAt(fieldIndex);
}
fieldIndex++;
+ if (!Objects.equals(mSelection, DEFAULT.mSelection)) {
+ nonDefaultFields |= bitAt(fieldIndex);
+ }
+ fieldIndex++;
if (mChecked != DEFAULT.mChecked) {
nonDefaultFields |= bitAt(fieldIndex);
}
@@ -5055,6 +5152,9 @@
parcel.writeLong(mLeashedParentNodeId);
}
if (isBitSet(nonDefaultFields, fieldIndex++)) {
+ mSelection.writeToParcel(parcel, flags);
+ }
+ if (isBitSet(nonDefaultFields, fieldIndex++)) {
parcel.writeInt(mChecked);
}
if (isBitSet(nonDefaultFields, fieldIndex++)) {
@@ -5172,6 +5272,17 @@
ExtraRenderingInfo ti = other.mExtraRenderingInfo;
mExtraRenderingInfo = (ti == null) ? null
: new ExtraRenderingInfo(ti);
+
+ if (Flags.a11ySelectionApi()) {
+ if (other.getSelection() != null) {
+ SelectionPosition sps = other.getSelection().getStart();
+ SelectionPosition spe = other.getSelection().getEnd();
+ mSelection =
+ new Selection(
+ new SelectionPosition(sps.mSourceNodeId, sps.getOffset()),
+ new SelectionPosition(spe.mSourceNodeId, spe.getOffset()));
+ }
+ }
}
/**
@@ -5344,6 +5455,9 @@
mLeashedParentNodeId = parcel.readLong();
}
if (isBitSet(nonDefaultFields, fieldIndex++)) {
+ mSelection = Selection.CREATOR.createFromParcel(parcel);
+ }
+ if (isBitSet(nonDefaultFields, fieldIndex++)) {
mChecked = parcel.readInt();
}
if (isBitSet(nonDefaultFields, fieldIndex++)) {
@@ -5495,6 +5609,9 @@
if (action == R.id.accessibilityActionScrollInDirection) {
return "ACTION_SCROLL_IN_DIRECTION";
}
+ if (action == R.id.accessibilityActionSetExtendedSelection) {
+ return "ACTION_SET_EXTENDED_SELECTION";
+ }
return "ACTION_UNKNOWN";
}
}
@@ -5696,6 +5813,271 @@
}
/**
+ * A class which defines either the start or end of a selection that can span across multiple
+ * AccessibilityNodeInfo objects.
+ *
+ * @see AccessibilityNodeInfo.Selection
+ */
+ @FlaggedApi(Flags.FLAG_A11Y_SELECTION_API)
+ public static final class SelectionPosition implements Parcelable {
+
+ private final int mOffset;
+ private final long mSourceNodeId;
+ private int mConnectionId;
+ private int mWindowId;
+
+ /**
+ * Instantiates a new SelectionPosition.
+ *
+ * @param node The {@link AccessibilityNodeInfo} for the node of this selection.
+ * @param offset The offset for a {@link SelectionPosition} within {@code view}'s text
+ * content, which should be a value between 0 and the length of {@code view}'s text.
+ */
+ public SelectionPosition(@NonNull AccessibilityNodeInfo node, int offset) {
+ this(node.mSourceNodeId, offset);
+ }
+
+ /**
+ * Instantiates a new SelectionPosition.
+ *
+ * @param view The {@link View} containing the virtual descendant associated with the
+ * selection position.
+ * @param offset The offset for a selection position within {@code view}'s text content,
+ * which should be a value between 0 and the length of {@code view}'s text.
+ */
+ public SelectionPosition(@NonNull View view, int offset) {
+ this(
+ makeNodeId(
+ view.getAccessibilityViewId(), AccessibilityNodeProvider.HOST_VIEW_ID),
+ offset);
+ }
+
+ /**
+ * Instantiates a new {@link SelectionPosition}.
+ *
+ * @param view The view whose virtual descendant is associated with the selection position.
+ * @param virtualDescendantId The ID of the virtual descendant within {@code view}'s virtual
+ * subtree that contains the selection position.
+ * @param offset The offset for a selection position within the virtual descendant's text
+ * content, which should be a value between 0 and the length of the descendant's text.
+ * @see AccessibilityNodeProvider
+ */
+ public SelectionPosition(@NonNull View view, int virtualDescendantId, int offset) {
+ this(makeNodeId(view.getAccessibilityViewId(), virtualDescendantId), offset);
+ }
+
+ private SelectionPosition(long sourceNodeId, int offset) {
+ mOffset = offset;
+ mSourceNodeId = sourceNodeId;
+ }
+
+ private SelectionPosition(Parcel in) {
+ mOffset = in.readInt();
+ mSourceNodeId = in.readLong();
+ }
+
+ private void setWindowId(int windowId) {
+ mWindowId = windowId;
+ }
+
+ private void setConnectionId(int connectionId) {
+ mConnectionId = connectionId;
+ }
+
+ /**
+ * Gets the node for {@code this} {@link SelectionPosition}
+ * <br>
+ * <strong>Note:</strong> This api can only be called from {@link AccessibilityService}.
+ *
+ * @return The node associated with {@code this} {@link SelectionPosition}
+ */
+ public @Nullable AccessibilityNodeInfo getNode() {
+ return getNodeForAccessibilityId(mConnectionId, mWindowId, mSourceNodeId);
+ }
+
+ /**
+ * Gets the offset for {@code this} {@link SelectionPosition}.
+ *
+ * @return A value from 0 to the length of {@link #getNode()}'s content representing the
+ * offset of the {@link SelectionPosition}
+ */
+ public int getOffset() {
+ return mOffset;
+ }
+
+ private boolean usesNode(@NonNull AccessibilityNodeInfo node) {
+ return this.mSourceNodeId == node.mSourceNodeId
+ && this.mConnectionId == node.mConnectionId
+ && this.mWindowId == node.mWindowId;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == null) {
+ return false;
+ }
+
+ if (other == this) {
+ return true;
+ }
+
+ if (getClass() != other.getClass()) {
+ return false;
+ }
+
+ SelectionPosition rhs = (SelectionPosition) other;
+ if (getOffset() != rhs.getOffset()) {
+ return false;
+ }
+
+ return mSourceNodeId == rhs.mSourceNodeId;
+ }
+
+ @Override
+ public int hashCode() {
+ final long prime = 877;
+ long result = 1;
+
+ if (mOffset != 0) {
+ result *= mOffset;
+ }
+
+ if (mSourceNodeId != UNDEFINED_NODE_ID) {
+ result *= mSourceNodeId;
+ }
+
+ return Long.hashCode(result * prime);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mOffset);
+ dest.writeLong(mSourceNodeId);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * @see android.os.Parcelable.Creator
+ */
+ @NonNull
+ public static final Creator<SelectionPosition> CREATOR =
+ new Creator<SelectionPosition>() {
+ @Override
+ public SelectionPosition createFromParcel(Parcel in) {
+ return new SelectionPosition(in);
+ }
+
+ @Override
+ public SelectionPosition[] newArray(int size) {
+ return new SelectionPosition[size];
+ }
+ };
+ }
+
+ /**
+ * Represents a selection of content that may extend across more than one {@link
+ * AccessibilityNodeInfo} instance.
+ *
+ * @see AccessibilityNodeInfo.SelectionPosition
+ */
+ @FlaggedApi(Flags.FLAG_A11Y_SELECTION_API)
+ public static final class Selection implements Parcelable {
+
+ private final SelectionPosition mStart;
+ private final SelectionPosition mEnd;
+
+ /**
+ * Instantiates a new Selection.
+ *
+ * @param start The start of the extended selection.
+ * @param end The end of the extended selection.
+ */
+ public Selection(@NonNull SelectionPosition start, @NonNull SelectionPosition end) {
+ this.mStart = start;
+ this.mEnd = end;
+ }
+
+ private Selection(Parcel in) {
+ mStart = SelectionPosition.CREATOR.createFromParcel(in);
+ mEnd = SelectionPosition.CREATOR.createFromParcel(in);
+ }
+
+ /**
+ * @return The start of the extended selection.
+ */
+ public @NonNull SelectionPosition getStart() {
+ return mStart;
+ }
+
+ /**
+ * @return The end of the extended selection.
+ */
+ public @NonNull SelectionPosition getEnd() {
+ return mEnd;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+
+ if (obj == this) {
+ return true;
+ }
+
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ Selection rhs = (Selection) obj;
+ return getStart().equals(rhs.getStart()) && getEnd().equals(rhs.getEnd());
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 17;
+ return prime * getStart().hashCode() * getEnd().hashCode();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ mStart.writeToParcel(dest, flags);
+ mEnd.writeToParcel(dest, flags);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * @see android.os.Parcelable.Creator
+ */
+ @NonNull
+ public static final Creator<Selection> CREATOR =
+ new Creator<Selection>() {
+ @Override
+ public Selection createFromParcel(Parcel in) {
+ return new Selection(in);
+ }
+
+ @Override
+ public Selection[] newArray(int size) {
+ return new Selection[size];
+ }
+ };
+ }
+
+ /**
* A class defining an action that can be performed on an {@link AccessibilityNodeInfo}.
* Each action has a unique id that is mandatory and optional data.
* <p>
@@ -6419,6 +6801,29 @@
@NonNull public static final AccessibilityAction ACTION_SHOW_TEXT_SUGGESTIONS =
new AccessibilityAction(R.id.accessibilityActionShowTextSuggestions);
+ /**
+ * Action to set the extended selection. Performing this action with no arguments clears the
+ * selection.
+ *
+ * <p><strong>Arguments:</strong> {@link
+ * AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_PARCELABLE
+ * AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_PARCELABLE}<br>
+ * <strong>Example:</strong> <code><pre><p>
+ * Bundle arguments = new Bundle();
+ * Selection selection = new Selection(null, null);
+ * arguments.setParcelable(
+ * AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_PARCELABLE, selection);
+ * info.performAction(
+ * AccessibilityAction.ACTION_SET_EXTENDED_SELECTION.getId(), arguments);
+ * </pre></code>
+ *
+ * @see AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_PARCELABLE
+ */
+ @FlaggedApi(Flags.FLAG_A11Y_SELECTION_API)
+ @NonNull
+ public static final AccessibilityAction ACTION_SET_EXTENDED_SELECTION =
+ new AccessibilityAction(R.id.accessibilityActionSetExtendedSelection);
+
private final int mActionId;
private final CharSequence mLabel;
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 9a5849a..641ecc9 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -399,7 +399,9 @@
@RemotableViewMethod(asyncImpl = "setIsCollapsedAsync")
public void setIsCollapsed(boolean isCollapsed) {
mIsCollapsed = isCollapsed;
- mMessagingLinearLayout.setMaxDisplayedLines(isCollapsed ? 1 : Integer.MAX_VALUE);
+ mMessagingLinearLayout.setMaxDisplayedLines(isCollapsed
+ ? TextUtils.isEmpty(mSummarizedContent) ? 1 : 2
+ : Integer.MAX_VALUE);
updateExpandButton();
updateContentEndPaddings();
}
@@ -448,7 +450,7 @@
List<MessagingMessage> newMessagingMessages;
mSummarizedContent = extras.getCharSequence(Notification.EXTRA_SUMMARIZED_CONTENT);
- if (mSummarizedContent != null && mIsCollapsed) {
+ if (!TextUtils.isEmpty(mSummarizedContent) && mIsCollapsed) {
Notification.MessagingStyle.Message summary =
new Notification.MessagingStyle.Message(mSummarizedContent, 0, "");
newMessagingMessages = createMessages(List.of(summary), false, usePrecomputedText);
@@ -1162,7 +1164,7 @@
nameOverride = mNameReplacement;
}
newGroup.setShowingAvatar(!mIsOneToOne && !mIsCollapsed);
- newGroup.setSingleLine(mIsCollapsed);
+ newGroup.setSingleLine(mIsCollapsed && TextUtils.isEmpty(mSummarizedContent));
newGroup.setSender(sender, nameOverride);
newGroup.setSending(groupIndex == (groups.size() - 1) && showSpinner);
mGroups.add(newGroup);
@@ -1462,7 +1464,6 @@
maxHeight = Math.max(maxHeight,
child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
}
-
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
if (maxHeight != getMeasuredHeight()) {
setMeasuredDimension(getMeasuredWidth(), maxHeight);
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index 90ab660..e9d920c 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -198,7 +198,8 @@
/* isHistoric= */true, usePrecomputedText);
List<MessagingMessage> newMessagingMessages;
mSummarizedContent = extras.getCharSequence(Notification.EXTRA_SUMMARIZED_CONTENT);
- if (mSummarizedContent != null && mIsCollapsed) {
+ if (!TextUtils.isEmpty(mSummarizedContent) && mIsCollapsed) {
+ mMessagingLinearLayout.setMaxDisplayedLines(2);
Notification.MessagingStyle.Message summary =
new Notification.MessagingStyle.Message(mSummarizedContent, 0, "");
newMessagingMessages = createMessages(List.of(summary), false, usePrecomputedText);
@@ -488,7 +489,7 @@
if (sender != mUser && mNameReplacement != null) {
nameOverride = mNameReplacement;
}
- newGroup.setSingleLine(mIsCollapsed);
+ newGroup.setSingleLine(mIsCollapsed && TextUtils.isEmpty(mSummarizedContent));
newGroup.setShowingAvatar(!mIsCollapsed);
newGroup.setSender(sender, nameOverride);
newGroup.setSending(groupIndex == (groups.size() - 1) && showSpinner);
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 447822f..06702e2 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -134,6 +134,10 @@
"android_app_ActivityThread.cpp",
"android_app_NativeActivity.cpp",
"android_app_admin_SecurityLog.cpp",
+ "android_media_ImageReader.cpp",
+ "android_media_ImageWriter.cpp",
+ "android_media_PublicFormatUtils.cpp",
+ "android_media_Utils.cpp",
"android_opengl_EGL14.cpp",
"android_opengl_EGL15.cpp",
"android_opengl_EGLExt.cpp",
@@ -531,3 +535,35 @@
"vintf",
],
}
+
+cc_library_shared {
+ name: "libmedia_jni_utils",
+ srcs: [
+ ":libgui_frame_event_aidl",
+ "android_media_Utils.cpp",
+ ],
+
+ header_libs: [
+ "libgui_headers",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libui",
+ "libutils",
+ ],
+
+ include_dirs: [
+ "system/media/camera/include",
+ ],
+
+ export_include_dirs: ["."],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-error=deprecated-declarations",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+}
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 5c0b720..b2b8263 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -102,7 +102,10 @@
extern int register_android_media_AudioProductStrategies(JNIEnv *env);
extern int register_android_media_AudioVolumeGroups(JNIEnv *env);
extern int register_android_media_AudioVolumeGroupChangeHandler(JNIEnv *env);
+extern int register_android_media_ImageReader(JNIEnv *env);
+extern int register_android_media_ImageWriter(JNIEnv *env);
extern int register_android_media_MicrophoneInfo(JNIEnv *env);
+extern int register_android_media_PublicFormatUtils(JNIEnv *env);
extern int register_android_media_ToneGenerator(JNIEnv *env);
extern int register_android_media_audio_common_AidlConversion(JNIEnv* env);
extern int register_android_media_midi(JNIEnv *env);
@@ -1658,8 +1661,11 @@
REG_JNI(register_android_media_AudioProductStrategies),
REG_JNI(register_android_media_AudioVolumeGroups),
REG_JNI(register_android_media_AudioVolumeGroupChangeHandler),
+ REG_JNI(register_android_media_ImageReader),
+ REG_JNI(register_android_media_ImageWriter),
REG_JNI(register_android_media_MediaMetrics),
REG_JNI(register_android_media_MicrophoneInfo),
+ REG_JNI(register_android_media_PublicFormatUtils),
REG_JNI(register_android_media_RemoteDisplay),
REG_JNI(register_android_media_ToneGenerator),
REG_JNI(register_android_media_audio_common_AidlConversion),
diff --git a/media/jni/android_media_ImageReader.cpp b/core/jni/android_media_ImageReader.cpp
similarity index 99%
rename from media/jni/android_media_ImageReader.cpp
rename to core/jni/android_media_ImageReader.cpp
index effa92c..20b9c57 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/core/jni/android_media_ImageReader.cpp
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#undef ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION // TODO:remove this and fix code
//#define LOG_NDEBUG 0
#define LOG_TAG "ImageReader_JNI"
diff --git a/media/jni/android_media_ImageWriter.cpp b/core/jni/android_media_ImageWriter.cpp
similarity index 99%
rename from media/jni/android_media_ImageWriter.cpp
rename to core/jni/android_media_ImageWriter.cpp
index 93deb51..1357dd8 100644
--- a/media/jni/android_media_ImageWriter.cpp
+++ b/core/jni/android_media_ImageWriter.cpp
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#undef ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION // TODO:remove this and fix code
//#define LOG_NDEBUG 0
#define LOG_TAG "ImageWriter_JNI"
diff --git a/media/jni/android_media_PublicFormatUtils.cpp b/core/jni/android_media_PublicFormatUtils.cpp
similarity index 100%
rename from media/jni/android_media_PublicFormatUtils.cpp
rename to core/jni/android_media_PublicFormatUtils.cpp
diff --git a/media/jni/android_media_Utils.cpp b/core/jni/android_media_Utils.cpp
similarity index 100%
rename from media/jni/android_media_Utils.cpp
rename to core/jni/android_media_Utils.cpp
diff --git a/media/jni/android_media_Utils.h b/core/jni/android_media_Utils.h
similarity index 100%
rename from media/jni/android_media_Utils.h
rename to core/jni/android_media_Utils.h
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 595160e..e9d87e4 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -431,15 +431,18 @@
<!-- The minimum height of the notification content (even when there's only one line of text) -->
<dimen name="notification_2025_content_min_height">40dp</dimen>
- <!-- Height of a headerless notification with one or two lines -->
- <!-- 16 * 2 (margins) + 40 (min content height) = 72 (notification) -->
+ <!-- Max height of a collapsed (headerless) notification with a summarization -->
+ <dimen name="notification_collapsed_height_with_summarization">156dp</dimen>
+
+ <!-- Max height of a collapsed (headerless) notification with one or two lines -->
+ <!-- 16 * 2 (margins) + 48 (min content height) = 72 (notification) -->
<dimen name="notification_2025_min_height">72dp</dimen>
<!-- Height of a headerless notification with one line -->
<!-- 16 * 2 (margins) + 24 (1 line) = 56 (notification) -->
<dimen name="notification_headerless_min_height">56dp</dimen>
- <!-- Height of a small two-line notification -->
+ <!-- Max height of a collapsed two-line notification -->
<!-- 20 * 2 (margins) + 24 * 2 (2 lines) = 88 (notification) -->
<dimen name="notification_min_height">88dp</dimen>
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 3b39a65..5b5ef09 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -157,6 +157,9 @@
<!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_CONTEXT_CLICK}. -->
<item type="id" name="accessibilityActionContextClick" />
+ <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_SET_EXTENDED_SELECTION}. -->
+ <item type="id" name="accessibilityActionSetExtendedSelection" />
+
<item type="id" name="remote_input_tag" />
<item type="id" name="pending_intent_tag" />
<item type="id" name="remote_checked_change_listener_tag" />
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 2d411d0..e3137e2 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -129,6 +129,8 @@
<staging-public-group type="id" first-id="0x01b20000">
<!-- @FlaggedApi(android.appwidget.flags.Flags.FLAG_ENGAGEMENT_METRICS) -->
<public name="remoteViewsMetricsId"/>
+ <!-- @FlaggedApi("android.view.accessibility.a11y_selection_api") -->
+ <public name="accessibilityActionSetExtendedSelection"/>
</staging-public-group>
<staging-public-group type="style" first-id="0x01b10000">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index cc2897a..da6fb3b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -5899,6 +5899,7 @@
<java-symbol type="array" name="config_notificationDefaultUnsupportedAdjustments" />
<java-symbol type="drawable" name="ic_notification_summarization" />
+ <java-symbol type="dimen" name="notification_collapsed_height_with_summarization" />
<!-- Advanced Protection Service USB feature -->
<java-symbol type="string" name="usb_apm_usb_plugged_in_when_locked_notification_title" />
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index 3b0eab4..cc5c6af 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
@@ -46,7 +46,7 @@
// The number of fields tested in the corresponding CTS AccessibilityNodeInfoTest:
// See fullyPopulateAccessibilityNodeInfo, assertEqualsAccessibilityNodeInfo,
// and assertAccessibilityNodeInfoCleared in that class.
- private static final int NUM_MARSHALLED_PROPERTIES = 47;
+ private static final int NUM_MARSHALLED_PROPERTIES = 48;
/**
* The number of properties that are purposely not marshalled
diff --git a/core/tests/coretests/src/com/android/internal/util/RateLimitingCacheTest.java b/core/tests/coretests/src/com/android/internal/util/RateLimitingCacheTest.java
index 52ff79d..2edab62 100644
--- a/core/tests/coretests/src/com/android/internal/util/RateLimitingCacheTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/RateLimitingCacheTest.java
@@ -22,6 +22,8 @@
import android.os.SystemClock;
+import android.os.SystemClock;
+
import androidx.test.runner.AndroidJUnit4;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -54,13 +56,13 @@
*/
@Test
public void testTtl_Zero() {
- TestRateLimitingCache<Integer> s = new TestRateLimitingCache<>(0);
+ RateLimitingCache<Integer> s = new RateLimitingCache<>(0);
int first = s.get(mFetcher);
assertEquals(first, 0);
int second = s.get(mFetcher);
assertEquals(second, 1);
- s.advanceTime(20);
+ SystemClock.sleep(20);
int third = s.get(mFetcher);
assertEquals(third, 2);
}
@@ -71,14 +73,14 @@
*/
@Test
public void testTtl_100() {
- TestRateLimitingCache<Integer> s = new TestRateLimitingCache<>(100);
+ RateLimitingCache<Integer> s = new RateLimitingCache<>(100);
int first = s.get(mFetcher);
assertEquals(first, 0);
int second = s.get(mFetcher);
// Too early to change
assertEquals(second, 0);
- s.advanceTime(150);
+ SystemClock.sleep(150);
int third = s.get(mFetcher);
// Changed by now
assertEquals(third, 1);
@@ -93,11 +95,11 @@
*/
@Test
public void testTtl_Negative() {
- TestRateLimitingCache<Integer> s = new TestRateLimitingCache<>(-1);
+ RateLimitingCache<Integer> s = new RateLimitingCache<>(-1);
int first = s.get(mFetcher);
assertEquals(first, 0);
- s.advanceTime(200);
+ SystemClock.sleep(200);
// Should return the original value every time
int second = s.get(mFetcher);
assertEquals(second, 0);
@@ -109,7 +111,7 @@
*/
@Test
public void testTtl_Spam() {
- TestRateLimitingCache<Integer> s = new TestRateLimitingCache<>(100);
+ RateLimitingCache<Integer> s = new RateLimitingCache<>(100);
assertCount(s, 1000, 7, 15);
}
@@ -119,13 +121,28 @@
*/
@Test
public void testRate_10hz() {
- TestRateLimitingCache<Integer> s = new TestRateLimitingCache<>(1000, 10);
+ RateLimitingCache<Integer> s = new RateLimitingCache<>(1000, 10);
// At 10 per second, 2 seconds should not exceed about 30, assuming overlap into left and
// right windows that allow 10 each
assertCount(s, 2000, 20, 33);
}
/**
+ * Test that using a different timebase works correctly.
+ */
+ @Test
+ public void testTimebase() {
+ RateLimitingCache<Integer> s = new RateLimitingCache<>(1000, 10) {
+ @Override
+ protected long getTime() {
+ return SystemClock.elapsedRealtime() / 2;
+ }
+ };
+ // Timebase is moving at half the speed, so only allows for 1 second worth in 2 seconds.
+ assertCount(s, 2000, 10, 22);
+ }
+
+ /**
* Exercises concurrent access to the cache.
*/
@Test
@@ -270,36 +287,15 @@
* @param minCount the lower end of the expected number of fetches, with a margin for error
* @param maxCount the higher end of the expected number of fetches, with a margin for error
*/
- private void assertCount(TestRateLimitingCache<Integer> cache, long period,
+ private void assertCount(RateLimitingCache<Integer> cache, long period,
int minCount, int maxCount) {
- long startTime = cache.getTime();
- while (cache.getTime() < startTime + period) {
+ long startTime = SystemClock.elapsedRealtime();
+ while (SystemClock.elapsedRealtime() < startTime + period) {
int value = cache.get(mFetcher);
- cache.advanceTime(5);
+ SystemClock.sleep(5);
}
int latest = cache.get(mFetcher);
assertTrue("Latest should be between " + minCount + " and " + maxCount
+ " but is " + latest, latest <= maxCount && latest >= minCount);
}
-
- private static class TestRateLimitingCache<Value> extends RateLimitingCache<Value> {
- private long mTime;
-
- public TestRateLimitingCache(long periodMillis) {
- super(periodMillis);
- }
-
- public TestRateLimitingCache(long periodMillis, int count) {
- super(periodMillis, count);
- }
-
- public void advanceTime(long time) {
- mTime += time;
- }
-
- @Override
- public long getTime() {
- return mTime;
- }
- }
}
diff --git a/graphics/java/android/graphics/FrameInfo.java b/graphics/java/android/graphics/FrameInfo.java
index 8f12828..7d236d2 100644
--- a/graphics/java/android/graphics/FrameInfo.java
+++ b/graphics/java/android/graphics/FrameInfo.java
@@ -95,7 +95,8 @@
// Must be the last one
// This value must be in sync with `UI_THREAD_FRAME_INFO_SIZE` in FrameInfo.h
- private static final int FRAME_INFO_SIZE = FRAME_INTERVAL + 1;
+ // In calculating size, + 1 for Flags, and + 1 for WorkloadTarget from FrameInfo.h
+ private static final int FRAME_INFO_SIZE = FRAME_INTERVAL + 2;
/** checkstyle */
public void setVsync(long intendedVsync, long usedVsync, long frameTimelineVsyncId,
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
index 50c0873..477d207 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
@@ -41,7 +41,7 @@
android:id="@+id/application_icon"
android:layout_width="@dimen/desktop_mode_caption_icon_radius"
android:layout_height="@dimen/desktop_mode_caption_icon_radius"
- android:layout_marginStart="12dp"
+ android:layout_marginStart="10dp"
android:layout_marginEnd="12dp"
android:contentDescription="@string/app_icon_text"
android:importantForAccessibility="no"/>
@@ -53,10 +53,9 @@
<com.android.wm.shell.windowdecor.HandleMenuImageButton
android:id="@+id/collapse_menu_button"
- android:layout_width="32dp"
- android:layout_height="32dp"
- android:padding="4dp"
- android:layout_marginEnd="14dp"
+ android:layout_width="16dp"
+ android:layout_height="16dp"
+ android:layout_marginEnd="16dp"
android:layout_marginStart="14dp"
android:contentDescription="@string/collapse_menu_text"
android:src="@drawable/ic_baseline_expand_more_24"
@@ -78,40 +77,55 @@
<ImageButton
android:id="@+id/fullscreen_button"
- android:layout_marginEnd="4dp"
+ android:paddingStart="16dp"
+ android:paddingEnd="12dp"
android:contentDescription="@string/fullscreen_text"
android:src="@drawable/desktop_mode_ic_handle_menu_fullscreen"
android:tint="@androidprv:color/materialColorOnSurface"
- android:layout_weight="1"
style="@style/DesktopModeHandleMenuWindowingButton"/>
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+
<ImageButton
android:id="@+id/split_screen_button"
- android:layout_marginStart="4dp"
- android:layout_marginEnd="4dp"
+ android:paddingStart="14dp"
+ android:paddingEnd="14dp"
android:contentDescription="@string/split_screen_text"
android:src="@drawable/desktop_mode_ic_handle_menu_splitscreen"
android:tint="@androidprv:color/materialColorOnSurface"
- android:layout_weight="1"
style="@style/DesktopModeHandleMenuWindowingButton"/>
+ <Space
+ android:id="@+id/floating_button_space"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+
<ImageButton
android:id="@+id/floating_button"
- android:layout_marginStart="4dp"
- android:layout_marginEnd="4dp"
+ android:paddingStart="14dp"
+ android:paddingEnd="14dp"
android:contentDescription="@string/float_button_text"
android:src="@drawable/desktop_mode_ic_handle_menu_floating"
android:tint="@androidprv:color/materialColorOnSurface"
- android:layout_weight="1"
style="@style/DesktopModeHandleMenuWindowingButton"/>
+ <Space
+ android:id="@+id/desktop_button_space"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+
<ImageButton
android:id="@+id/desktop_button"
- android:layout_marginStart="4dp"
+ android:paddingStart="12dp"
+ android:paddingEnd="16dp"
android:contentDescription="@string/desktop_text"
android:src="@drawable/desktop_mode_ic_handle_menu_desktop"
android:tint="@androidprv:color/materialColorOnSurface"
- android:layout_weight="1"
style="@style/DesktopModeHandleMenuWindowingButton"/>
</LinearLayout>
@@ -126,77 +140,33 @@
android:elevation="@dimen/desktop_mode_handle_menu_pill_elevation"
android:background="@drawable/desktop_mode_decor_handle_menu_background">
- <LinearLayout
+ <com.android.wm.shell.windowdecor.HandleMenuActionButton
android:id="@+id/screenshot_button"
android:contentDescription="@string/screenshot_text"
- style="@style/DesktopModeHandleMenuActionButtonLayout">
+ android:text="@string/screenshot_text"
+ android:src="@drawable/desktop_mode_ic_handle_menu_screenshot"
+ style="@style/DesktopModeHandleMenuActionButton"/>
- <ImageView
- android:id="@+id/image"
- android:src="@drawable/desktop_mode_ic_handle_menu_screenshot"
- android:importantForAccessibility="no"
- style="@style/DesktopModeHandleMenuActionButtonImage"/>
-
- <com.android.wm.shell.windowdecor.MarqueedTextView
- android:id="@+id/label"
- android:text="@string/screenshot_text"
- style="@style/DesktopModeHandleMenuActionButtonTextView"/>
-
- </LinearLayout>
-
- <LinearLayout
+ <com.android.wm.shell.windowdecor.HandleMenuActionButton
android:id="@+id/new_window_button"
android:contentDescription="@string/new_window_text"
- style="@style/DesktopModeHandleMenuActionButtonLayout">
+ android:text="@string/new_window_text"
+ android:src="@drawable/desktop_mode_ic_handle_menu_new_window"
+ style="@style/DesktopModeHandleMenuActionButton"/>
- <ImageView
- android:id="@+id/image"
- android:src="@drawable/desktop_mode_ic_handle_menu_new_window"
- android:importantForAccessibility="no"
- style="@style/DesktopModeHandleMenuActionButtonImage"/>
-
- <com.android.wm.shell.windowdecor.MarqueedTextView
- android:id="@+id/label"
- android:text="@string/new_window_text"
- style="@style/DesktopModeHandleMenuActionButtonTextView"/>
-
- </LinearLayout>
-
- <LinearLayout
+ <com.android.wm.shell.windowdecor.HandleMenuActionButton
android:id="@+id/manage_windows_button"
android:contentDescription="@string/manage_windows_text"
- style="@style/DesktopModeHandleMenuActionButtonLayout">
+ android:text="@string/manage_windows_text"
+ android:src="@drawable/desktop_mode_ic_handle_menu_manage_windows"
+ style="@style/DesktopModeHandleMenuActionButton"/>
- <ImageView
- android:id="@+id/image"
- android:src="@drawable/desktop_mode_ic_handle_menu_manage_windows"
- android:importantForAccessibility="no"
- style="@style/DesktopModeHandleMenuActionButtonImage"/>
-
- <com.android.wm.shell.windowdecor.MarqueedTextView
- android:id="@+id/label"
- android:text="@string/manage_windows_text"
- style="@style/DesktopModeHandleMenuActionButtonTextView"/>
-
- </LinearLayout>
-
- <LinearLayout
+ <com.android.wm.shell.windowdecor.HandleMenuActionButton
android:id="@+id/change_aspect_ratio_button"
android:contentDescription="@string/change_aspect_ratio_text"
- style="@style/DesktopModeHandleMenuActionButtonLayout">
-
- <ImageView
- android:id="@+id/image"
- android:src="@drawable/desktop_mode_ic_handle_menu_change_aspect_ratio"
- android:importantForAccessibility="no"
- style="@style/DesktopModeHandleMenuActionButtonImage"/>
-
- <com.android.wm.shell.windowdecor.MarqueedTextView
- android:id="@+id/label"
- android:text="@string/change_aspect_ratio_text"
- style="@style/DesktopModeHandleMenuActionButtonTextView"/>
-
- </LinearLayout>
+ android:text="@string/change_aspect_ratio_text"
+ android:src="@drawable/desktop_mode_ic_handle_menu_change_aspect_ratio"
+ style="@style/DesktopModeHandleMenuActionButton"/>
</LinearLayout>
<LinearLayout
@@ -209,29 +179,14 @@
android:elevation="@dimen/desktop_mode_handle_menu_pill_elevation"
android:background="@drawable/desktop_mode_decor_handle_menu_background">
- <LinearLayout
+ <com.android.wm.shell.windowdecor.HandleMenuActionButton
android:id="@+id/open_in_app_or_browser_button"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:layout_marginEnd="8dp"
- android:gravity="start|center_vertical"
- android:paddingStart="16dp"
android:contentDescription="@string/open_in_browser_text"
- android:background="?android:selectableItemBackground">
-
- <ImageView
- android:id="@+id/image"
- android:src="@drawable/desktop_mode_ic_handle_menu_open_in_browser"
- android:importantForAccessibility="no"
- style="@style/DesktopModeHandleMenuActionButtonImage"/>
-
- <com.android.wm.shell.windowdecor.MarqueedTextView
- android:id="@+id/label"
- android:text="@string/open_in_browser_text"
- style="@style/DesktopModeHandleMenuActionButtonTextView"/>
-
- </LinearLayout>
+ android:text="@string/open_in_browser_text"
+ android:src="@drawable/desktop_mode_ic_handle_menu_open_in_browser"
+ style="@style/DesktopModeHandleMenuActionButton"
+ android:layout_width="0dp"
+ android:layout_weight="1"/>
<ImageButton
android:id="@+id/open_by_default_button"
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml
new file mode 100644
index 0000000..379f4e9
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/action_button"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="start|center_vertical"
+ android:paddingHorizontal="16dp"
+ android:clickable="true"
+ android:focusable="true"
+ android:orientation="horizontal"
+ android:background="?android:attr/selectableItemBackground">
+
+ <ImageView
+ android:id="@+id/image"
+ android:contentDescription="@+id/label"
+ style="@style/DesktopModeHandleMenuActionButtonImage"/>
+
+ <com.android.wm.shell.windowdecor.MarqueedTextView
+ android:id="@+id/label"
+ style="@style/DesktopModeHandleMenuActionButtonTextView"/>
+</LinearLayout>
diff --git a/libs/WindowManager/Shell/res/values/attrs.xml b/libs/WindowManager/Shell/res/values/attrs.xml
index fbb5caa..4ba0468 100644
--- a/libs/WindowManager/Shell/res/values/attrs.xml
+++ b/libs/WindowManager/Shell/res/values/attrs.xml
@@ -23,4 +23,11 @@
<declare-styleable name="MessageState">
<attr name="state_task_focused" format="boolean"/>
</declare-styleable>
+
+ <declare-styleable name="HandleMenuActionButton">
+ <attr name="android:text" format="string" />
+ <attr name="android:textColor" format="color" />
+ <attr name="android:src" format="reference" />
+ <attr name="android:drawableTint" format="color" />
+ </declare-styleable>
</resources>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index e23d572..96e008e 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -570,7 +570,7 @@
<dimen name="desktop_mode_handle_menu_corner_radius">26dp</dimen>
<!-- The radius of the caption menu icon. -->
- <dimen name="desktop_mode_caption_icon_radius">24dp</dimen>
+ <dimen name="desktop_mode_caption_icon_radius">32dp</dimen>
<!-- The radius of the caption menu shadow. -->
<dimen name="desktop_mode_handle_menu_shadow_radius">2dp</dimen>
diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml
index 035004b..637b47a 100644
--- a/libs/WindowManager/Shell/res/values/styles.xml
+++ b/libs/WindowManager/Shell/res/values/styles.xml
@@ -40,13 +40,11 @@
<item name="android:activityCloseExitAnimation">@anim/forced_resizable_exit</item>
</style>
- <style name="DesktopModeHandleMenuActionButtonLayout">
+ <style name="DesktopModeHandleMenuActionButton">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">52dp</item>
- <item name="android:layout_weight">1</item>
- <item name="android:gravity">start|center_vertical</item>
- <item name="android:paddingHorizontal">16dp</item>
- <item name="android:background">?android:selectableItemBackground</item>
+ <item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
+ <item name="android:drawableTint">@androidprv:color/materialColorOnSurface</item>
</style>
<style name="DesktopModeHandleMenuActionButtonImage">
@@ -71,7 +69,6 @@
<style name="DesktopModeHandleMenuWindowingButton">
<item name="android:layout_width">48dp</item>
<item name="android:layout_height">48dp</item>
- <item name="android:padding">14dp</item>
<item name="android:scaleType">fitCenter</item>
<item name="android:background">?android:selectableItemBackgroundBorderless</item>
</style>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
index a17bcb3..ff50672 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
@@ -37,6 +37,9 @@
import android.view.WindowManager
import android.widget.ImageButton
import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.Space
+import android.widget.TextView
import android.window.DesktopModeFlags
import android.window.SurfaceSyncGroup
import androidx.annotation.StringRes
@@ -480,16 +483,23 @@
private val splitscreenBtn = windowingPill.requireViewById<ImageButton>(
R.id.split_screen_button)
private val floatingBtn = windowingPill.requireViewById<ImageButton>(R.id.floating_button)
+ private val floatingBtnSpace = windowingPill.requireViewById<Space>(
+ R.id.floating_button_space)
+
private val desktopBtn = windowingPill.requireViewById<ImageButton>(R.id.desktop_button)
+ private val desktopBtnSpace = windowingPill.requireViewById<Space>(
+ R.id.desktop_button_space)
// More Actions Pill.
private val moreActionsPill = rootView.requireViewById<View>(R.id.more_actions_pill)
- private val screenshotBtn = moreActionsPill.requireViewById<View>(R.id.screenshot_button)
- private val newWindowBtn = moreActionsPill.requireViewById<View>(R.id.new_window_button)
+ private val screenshotBtn = moreActionsPill.requireViewById<HandleMenuActionButton>(
+ R.id.screenshot_button)
+ private val newWindowBtn = moreActionsPill.requireViewById<HandleMenuActionButton>(
+ R.id.new_window_button)
private val manageWindowBtn = moreActionsPill
- .requireViewById<View>(R.id.manage_windows_button)
+ .requireViewById<HandleMenuActionButton>(R.id.manage_windows_button)
private val changeAspectRatioBtn = moreActionsPill
- .requireViewById<View>(R.id.change_aspect_ratio_button)
+ .requireViewById<HandleMenuActionButton>(R.id.change_aspect_ratio_button)
// Open in Browser/App Pill.
private val openInAppOrBrowserPill = rootView.requireViewById<View>(
@@ -682,6 +692,7 @@
if (!BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
floatingBtn.visibility = View.GONE
+ floatingBtnSpace.visibility = View.GONE
}
fullscreenBtn.isSelected = taskInfo.isFullscreen
@@ -710,8 +721,10 @@
).forEach {
val button = it.first
val shouldShow = it.second
- val label = button.requireViewById<MarqueedTextView>(R.id.label)
- val image = button.requireViewById<ImageView>(R.id.image)
+
+ val buttonRoot = button.requireViewById<LinearLayout>(R.id.action_button)
+ val label = buttonRoot.requireViewById<MarqueedTextView>(R.id.label)
+ val image = buttonRoot.requireViewById<ImageView>(R.id.image)
button.isGone = !shouldShow
label.apply {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuActionButton.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuActionButton.kt
new file mode 100644
index 0000000..4b2e473
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuActionButton.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.windowdecor
+
+import android.annotation.ColorInt
+import android.annotation.IdRes
+import android.content.Context
+import android.content.res.ColorStateList
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.TextView
+import androidx.core.content.withStyledAttributes
+import androidx.core.view.isGone
+import com.android.wm.shell.R
+
+/**
+ * Button-like component used to display the "Additional options" elements of the Handle menu window
+ * decoration.
+ *
+ * The possible options for which this button is used for are "Screenshot", "New Window", "Manage
+ * Windows" and "Change Aspect Ratio".
+ */
+class HandleMenuActionButton @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0
+) : LinearLayout(context, attrs, defStyleAttr) {
+
+ private val rootElement: LinearLayout
+ private val iconView: ImageView
+ private val textView: MarqueedTextView
+
+ init {
+ val view = LayoutInflater.from(context).inflate(
+ R.layout.desktop_mode_window_decor_handle_menu_action_button, this, true)
+ rootElement = findViewById(R.id.action_button)
+ iconView = findViewById(R.id.image)
+ textView = findViewById(R.id.label)
+
+ context.withStyledAttributes(attrs, R.styleable.HandleMenuActionButton) {
+ textView.text = getString(R.styleable.HandleMenuActionButton_android_text)
+ textView.setTextColor(getColor(R.styleable.HandleMenuActionButton_android_textColor, 0))
+ iconView.setImageResource(getResourceId(
+ R.styleable.HandleMenuActionButton_android_src, 0))
+ iconView.imageTintList = getColorStateList(
+ R.styleable.HandleMenuActionButton_android_drawableTint)
+ }
+ }
+
+ /**
+ * Sets a listener to be invoked when this view is clicked.
+ *
+ * @param l the [OnClickListener] that receives click events.
+ */
+ override fun setOnClickListener(l: OnClickListener?) {
+ rootElement.setOnClickListener(l)
+ }
+
+ /**
+ * Sets the text color for the text inside the button.
+ *
+ * @param color the color to set for the text, as a color integer.
+ */
+ fun setTextColor(@ColorInt color: Int) {
+ textView.setTextColor(color)
+ }
+
+ /**
+ * Sets the icon for the button using a resource ID.
+ *
+ * @param resourceId the resource ID of the drawable to set as the icon.
+ */
+ fun setIconResource(@IdRes resourceId: Int) {
+ iconView.setImageResource(resourceId)
+ }
+
+ /**
+ * Sets the text to display inside the button.
+ *
+ * @param text the text to display.
+ */
+ fun setText(text: CharSequence?) {
+ textView.text = text
+ }
+
+ /**
+ * Sets the tint color for the icon.
+ *
+ * @param color the color to use for the tint, as a color integer.
+ */
+ fun setDrawableTint(@ColorInt color: Int) {
+ iconView.imageTintList = ColorStateList.valueOf(color)
+ }
+
+ /**
+ * Gets or sets the tint applied to the icon.
+ *
+ * @return The [ColorStateList] representing the tint, or null if no tint is applied.
+ */
+ var compoundDrawableTintList: ColorStateList?
+ get() = iconView.imageTintList
+ set(value) {
+ iconView.imageTintList = value
+ }
+}
diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp
index a958a09..36feabd 100644
--- a/libs/hwui/FrameInfo.cpp
+++ b/libs/hwui/FrameInfo.cpp
@@ -32,8 +32,9 @@
"PerformTraversalsStart",
"DrawStart",
"FrameDeadline",
- "FrameInterval",
"FrameStartTime",
+ "FrameInterval",
+ "WorkloadTarget",
"SyncQueued",
"SyncStart",
"IssueDrawCommandsStart",
@@ -48,7 +49,7 @@
};
-static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 23,
+static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 24,
"Must update value in FrameMetrics.java#FRAME_STATS_COUNT (and here)");
void FrameInfo::importUiThreadInfo(int64_t* info) {
diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h
index f7ad139..61c30b8 100644
--- a/libs/hwui/FrameInfo.h
+++ b/libs/hwui/FrameInfo.h
@@ -30,7 +30,8 @@
namespace android {
namespace uirenderer {
-static constexpr size_t UI_THREAD_FRAME_INFO_SIZE = 12;
+// This value must be in sync with `FRAME_INFO_SIZE` in FrameInfo.Java
+static constexpr size_t UI_THREAD_FRAME_INFO_SIZE = 13;
enum class FrameInfoIndex {
Flags = 0,
@@ -47,6 +48,11 @@
FrameInterval,
// End of UI frame info
+ // The target workload duration based on the original frame deadline and
+ // and intended vsync. Counted in UI_THREAD_FRAME_INFO_SIZE so its value
+ // can be set in setVsync().
+ WorkloadTarget,
+
SyncQueued,
SyncStart,
@@ -109,6 +115,7 @@
set(FrameInfoIndex::FrameStartTime) = vsyncTime;
set(FrameInfoIndex::FrameDeadline) = frameDeadline;
set(FrameInfoIndex::FrameInterval) = frameInterval;
+ set(FrameInfoIndex::WorkloadTarget) = frameDeadline - intendedVsync;
return *this;
}
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index 638a060..80eb6bc 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -201,7 +201,7 @@
// If we are in triple buffering, we have enough buffers in queue to sustain a single frame
// drop without jank, so adjust the frame interval to the deadline.
if (isTripleBuffered) {
- int64_t originalDeadlineDuration = deadline - frame[FrameInfoIndex::IntendedVsync];
+ int64_t originalDeadlineDuration = frame[FrameInfoIndex::WorkloadTarget];
deadline = mNextFrameStartUnstuffed + originalDeadlineDuration;
frame.set(FrameInfoIndex::FrameDeadline) = deadline;
}
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 7d01dfb..21430f7 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -57,6 +57,9 @@
constexpr bool early_preload_gl_context() {
return false;
}
+constexpr bool calc_workload_orig_deadline() {
+ return false;
+}
} // namespace hwui_flags
#endif
@@ -299,5 +302,10 @@
hwui_flags::early_preload_gl_context());
}
+bool Properties::calcWorkloadOrigDeadline() {
+ static bool sCalcWorkloadOrigDeadline = base::GetBoolProperty(
+ "debug.hwui.calc_workload_orig_deadline", hwui_flags::calc_workload_orig_deadline());
+ return sCalcWorkloadOrigDeadline;
+}
} // namespace uirenderer
} // namespace android
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 280a75a..a7a5644 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -384,6 +384,7 @@
static bool initializeGlAlways();
static bool resampleGainmapRegions();
static bool earlyPreloadGlContext();
+ static bool calcWorkloadOrigDeadline();
private:
static StretchEffectBehavior stretchEffectBehavior;
diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig
index 2851dd8..62fd7d3 100644
--- a/libs/hwui/aconfig/hwui_flags.aconfig
+++ b/libs/hwui/aconfig/hwui_flags.aconfig
@@ -176,4 +176,15 @@
namespace: "core_graphics"
description: "Initialize GL context and GraphicBufferAllocater init on renderThread preload. This improves app startup time for apps using GL."
bug: "383612849"
+}
+
+flag {
+ name: "calc_workload_orig_deadline"
+ namespace: "window_surfaces"
+ description: "Use original frame deadline to calculate the workload target deadline for jank tracking"
+ bug: "389939827"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
\ No newline at end of file
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index b36b8be..e3e393c 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -789,7 +789,13 @@
int64_t frameDeadline = mCurrentFrameInfo->get(FrameInfoIndex::FrameDeadline);
int64_t dequeueBufferDuration = mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration);
- mHintSessionWrapper->updateTargetWorkDuration(frameDeadline - intendedVsync);
+ if (Properties::calcWorkloadOrigDeadline()) {
+ // Uses the unmodified frame deadline in calculating workload target duration
+ mHintSessionWrapper->updateTargetWorkDuration(
+ mCurrentFrameInfo->get(FrameInfoIndex::WorkloadTarget));
+ } else {
+ mHintSessionWrapper->updateTargetWorkDuration(frameDeadline - intendedVsync);
+ }
if (didDraw) {
int64_t frameStartTime = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime);
diff --git a/libs/hwui/tests/unit/JankTrackerTests.cpp b/libs/hwui/tests/unit/JankTrackerTests.cpp
index b67e419..c289d67 100644
--- a/libs/hwui/tests/unit/JankTrackerTests.cpp
+++ b/libs/hwui/tests/unit/JankTrackerTests.cpp
@@ -45,6 +45,7 @@
info->set(FrameInfoIndex::FrameCompleted) = 115_ms;
info->set(FrameInfoIndex::FrameInterval) = 16_ms;
info->set(FrameInfoIndex::FrameDeadline) = 120_ms;
+ info->set(FrameInfoIndex::WorkloadTarget) = 20_ms;
jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
info = jankTracker.startFrame();
@@ -55,6 +56,7 @@
info->set(FrameInfoIndex::FrameCompleted) = 131_ms;
info->set(FrameInfoIndex::FrameInterval) = 16_ms;
info->set(FrameInfoIndex::FrameDeadline) = 136_ms;
+ info->set(FrameInfoIndex::WorkloadTarget) = 20_ms;
jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
ASSERT_EQ(2, container.get()->totalFrameCount());
@@ -79,6 +81,7 @@
info->set(FrameInfoIndex::FrameCompleted) = 121_ms;
info->set(FrameInfoIndex::FrameInterval) = 16_ms;
info->set(FrameInfoIndex::FrameDeadline) = 120_ms;
+ info->set(FrameInfoIndex::WorkloadTarget) = 20_ms;
jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
ASSERT_EQ(1, container.get()->totalFrameCount());
@@ -102,6 +105,7 @@
info->set(FrameInfoIndex::FrameCompleted) = 118_ms;
info->set(FrameInfoIndex::FrameInterval) = 16_ms;
info->set(FrameInfoIndex::FrameDeadline) = 120_ms;
+ info->set(FrameInfoIndex::WorkloadTarget) = 20_ms;
jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
ASSERT_EQ(1, container.get()->totalFrameCount());
@@ -127,6 +131,7 @@
info->set(FrameInfoIndex::FrameCompleted) = 121_ms;
info->set(FrameInfoIndex::FrameInterval) = 16_ms;
info->set(FrameInfoIndex::FrameDeadline) = 120_ms;
+ info->set(FrameInfoIndex::WorkloadTarget) = 20_ms;
jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
ASSERT_EQ(1, container.get()->jankFrameCount());
@@ -140,6 +145,7 @@
info->set(FrameInfoIndex::FrameCompleted) = 137_ms;
info->set(FrameInfoIndex::FrameInterval) = 16_ms;
info->set(FrameInfoIndex::FrameDeadline) = 136_ms;
+ info->set(FrameInfoIndex::WorkloadTarget) = 20_ms;
jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
ASSERT_EQ(2, container.get()->totalFrameCount());
@@ -164,6 +170,7 @@
info->set(FrameInfoIndex::FrameCompleted) = 121_ms;
info->set(FrameInfoIndex::FrameInterval) = 16_ms;
info->set(FrameInfoIndex::FrameDeadline) = 120_ms;
+ info->set(FrameInfoIndex::WorkloadTarget) = 20_ms;
jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
ASSERT_EQ(1, container.get()->jankFrameCount());
@@ -177,6 +184,7 @@
info->set(FrameInfoIndex::FrameCompleted) = 137_ms;
info->set(FrameInfoIndex::FrameInterval) = 16_ms;
info->set(FrameInfoIndex::FrameDeadline) = 136_ms;
+ info->set(FrameInfoIndex::WorkloadTarget) = 20_ms;
jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
ASSERT_EQ(1, container.get()->jankFrameCount());
@@ -190,6 +198,7 @@
info->set(FrameInfoIndex::FrameCompleted) = 169_ms;
info->set(FrameInfoIndex::FrameInterval) = 16_ms;
info->set(FrameInfoIndex::FrameDeadline) = 168_ms;
+ info->set(FrameInfoIndex::WorkloadTarget) = 20_ms;
jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
ASSERT_EQ(3, container.get()->totalFrameCount());
@@ -214,6 +223,7 @@
info->set(FrameInfoIndex::FrameCompleted) = 117_ms;
info->set(FrameInfoIndex::FrameInterval) = 16_ms;
info->set(FrameInfoIndex::FrameDeadline) = 116_ms;
+ info->set(FrameInfoIndex::WorkloadTarget) = 16_ms;
jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
ASSERT_EQ(1, container.get()->jankFrameCount());
@@ -228,6 +238,7 @@
info->set(FrameInfoIndex::FrameCompleted) = 133_ms;
info->set(FrameInfoIndex::FrameInterval) = 16_ms;
info->set(FrameInfoIndex::FrameDeadline) = 132_ms;
+ info->set(FrameInfoIndex::WorkloadTarget) = 16_ms;
jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
ASSERT_EQ(1, container.get()->jankFrameCount());
@@ -242,6 +253,7 @@
info->set(FrameInfoIndex::FrameCompleted) = 165_ms;
info->set(FrameInfoIndex::FrameInterval) = 16_ms;
info->set(FrameInfoIndex::FrameDeadline) = 148_ms;
+ info->set(FrameInfoIndex::WorkloadTarget) = 16_ms;
jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
ASSERT_EQ(2, container.get()->jankFrameCount());
@@ -256,6 +268,7 @@
info->set(FrameInfoIndex::FrameCompleted) = 181_ms;
info->set(FrameInfoIndex::FrameInterval) = 16_ms;
info->set(FrameInfoIndex::FrameDeadline) = 164_ms;
+ info->set(FrameInfoIndex::WorkloadTarget) = 16_ms;
jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
ASSERT_EQ(2, container.get()->jankFrameCount());
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index f09dc72..6b72173 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -25,8 +25,6 @@
min_sdk_version: "",
srcs: [
- "android_media_ImageWriter.cpp",
- "android_media_ImageReader.cpp",
"android_media_JetPlayer.cpp",
"android_media_MediaCrypto.cpp",
"android_media_MediaCodec.cpp",
@@ -42,7 +40,6 @@
"android_media_MediaProfiles.cpp",
"android_media_MediaRecorder.cpp",
"android_media_MediaSync.cpp",
- "android_media_PublicFormatUtils.cpp",
"android_media_ResampleInputStream.cpp",
"android_media_Streams.cpp",
"android_media_SyncParams.cpp",
@@ -64,7 +61,6 @@
"libbinder",
"libmedia",
"libmedia_codeclist",
- "libmedia_jni_utils",
"libmedia_omx",
"libmediametrics",
"libmediadrm",
@@ -133,38 +129,6 @@
}
cc_library_shared {
- name: "libmedia_jni_utils",
- srcs: [
- ":libgui_frame_event_aidl",
- "android_media_Utils.cpp",
- ],
-
- header_libs: [
- "libgui_headers",
- ],
-
- shared_libs: [
- "liblog",
- "libui",
- "libutils",
- ],
-
- include_dirs: [
- "system/media/camera/include",
- ],
-
- export_include_dirs: ["."],
-
- cflags: [
- "-Wall",
- "-Werror",
- "-Wno-error=deprecated-declarations",
- "-Wunused",
- "-Wunreachable-code",
- ],
-}
-
-cc_library_shared {
name: "libmedia_tv_tuner",
min_sdk_version: "",
srcs: [
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index a942300..647b553 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -1476,8 +1476,6 @@
return AndroidRuntime::registerNativeMethods(env,
"android/media/MediaPlayer", gMethods, NELEM(gMethods));
}
-extern int register_android_media_ImageReader(JNIEnv *env);
-extern int register_android_media_ImageWriter(JNIEnv *env);
extern int register_android_media_JetPlayer(JNIEnv *env);
extern int register_android_media_Crypto(JNIEnv *env);
extern int register_android_media_Drm(JNIEnv *env);
@@ -1490,7 +1488,6 @@
extern int register_android_media_MediaMuxer(JNIEnv *env);
extern int register_android_media_MediaRecorder(JNIEnv *env);
extern int register_android_media_MediaSync(JNIEnv *env);
-extern int register_android_media_PublicFormatUtils(JNIEnv *env);
extern int register_android_media_ResampleInputStream(JNIEnv *env);
extern int register_android_media_MediaProfiles(JNIEnv *env);
extern int register_android_mtp_MtpDatabase(JNIEnv *env);
@@ -1508,16 +1505,6 @@
}
assert(env != NULL);
- if (register_android_media_ImageWriter(env) != JNI_OK) {
- ALOGE("ERROR: ImageWriter native registration failed");
- goto bail;
- }
-
- if (register_android_media_ImageReader(env) < 0) {
- ALOGE("ERROR: ImageReader native registration failed");
- goto bail;
- }
-
if (register_android_media_JetPlayer(env) < 0) {
ALOGE("ERROR: JetPlayer native registration failed");
goto bail;
@@ -1538,11 +1525,6 @@
goto bail;
}
- if (register_android_media_PublicFormatUtils(env) < 0) {
- ALOGE("ERROR: PublicFormatUtils native registration failed\n");
- goto bail;
- }
-
if (register_android_media_ResampleInputStream(env) < 0) {
ALOGE("ERROR: ResampleInputStream native registration failed\n");
goto bail;
diff --git a/packages/PrintSpooler/src/com/android/printspooler/widget/PreviewPageFrame.java b/packages/PrintSpooler/src/com/android/printspooler/widget/PreviewPageFrame.java
index 95bdb09..e735610 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/widget/PreviewPageFrame.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/widget/PreviewPageFrame.java
@@ -16,6 +16,8 @@
package com.android.printspooler.widget;
+import static android.view.accessibility.Flags.triStateChecked;
+
import android.content.Context;
import android.util.AttributeSet;
import android.view.accessibility.AccessibilityEvent;
@@ -38,6 +40,20 @@
}
@Override
+ public boolean performClick() {
+ final boolean result = super.performClick();
+ // This widget is incorrectly using the notion of "selection"
+ // to represent checked state. We can't send this event in
+ // setSelected() because setSelected() is called when this widget
+ // is not attached.
+ if (triStateChecked()) {
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_CHECKED);
+ }
+ return result;
+ }
+
+ @Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
event.setChecked(isSelected());
@@ -48,6 +64,11 @@
super.onInitializeAccessibilityNodeInfo(info);
info.setSelected(false);
info.setCheckable(true);
- info.setChecked(isSelected());
+ if (triStateChecked()) {
+ info.setChecked(isSelected() ? AccessibilityNodeInfo.CHECKED_STATE_TRUE :
+ AccessibilityNodeInfo.CHECKED_STATE_FALSE);
+ } else {
+ info.setChecked(isSelected());
+ }
}
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/display/DisplayDensityUtilsTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/display/DisplayDensityUtilsTest.java
new file mode 100644
index 0000000..bba278a
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/display/DisplayDensityUtilsTest.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.display;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerGlobal;
+import android.os.RemoteException;
+import android.view.Display;
+import android.view.DisplayAdjustments;
+import android.view.DisplayInfo;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.settingslib.R;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class DisplayDensityUtilsTest {
+
+ private static final float MAX_SCALE = 1.33f;
+ private static final float MIN_SCALE = 0.85f;
+ private static final float MIN_INTERVAL = 0.09f;
+ @Mock
+ private Context mContext;
+ @Mock
+ private Resources mResources;
+ @Mock
+ private DisplayManager mDisplayManager;
+ @Mock
+ private DisplayManagerGlobal mDisplayManagerGlobal;
+ @Mock
+ private IWindowManager mIWindowManager;
+ private IWindowManager mWindowManagerToRestore;
+ private DisplayDensityUtils mDisplayDensityUtils;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ doReturn(mDisplayManager).when(mContext).getSystemService((Class<Object>) any());
+ mWindowManagerToRestore = WindowManagerGlobal.getWindowManagerService();
+ WindowManagerGlobal.setWindowManagerServiceForSystemProcess(mIWindowManager);
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mResources.getFraction(R.fraction.display_density_max_scale, 1, 1)).thenReturn(
+ MAX_SCALE);
+ when(mResources.getFraction(R.fraction.display_density_min_scale, 1, 1)).thenReturn(
+ MIN_SCALE);
+ when(mResources.getFraction(R.fraction.display_density_min_scale_interval, 1,
+ 1)).thenReturn(MIN_INTERVAL);
+ when(mResources.getString(anyInt())).thenReturn("summary");
+ }
+
+ @After
+ public void teardown() {
+ WindowManagerGlobal.setWindowManagerServiceForSystemProcess(mWindowManagerToRestore);
+ }
+
+ @Test
+ public void createDisplayDensityUtil_onlyDefaultDisplay() throws RemoteException {
+ var info = createDisplayInfoForDisplay(Display.DEFAULT_DISPLAY, Display.TYPE_INTERNAL, 2560,
+ 1600, 320);
+ var display = new Display(mDisplayManagerGlobal, info.displayId, info,
+ (DisplayAdjustments) null);
+ doReturn(new Display[]{display}).when(mDisplayManager).getDisplays(any());
+ doReturn(display).when(mDisplayManager).getDisplay(info.displayId);
+
+ mDisplayDensityUtils = new DisplayDensityUtils(mContext);
+
+ assertThat(mDisplayDensityUtils.getValues()).isEqualTo(new int[]{272, 320, 354, 390, 424});
+ }
+
+ @Test
+ public void createDisplayDensityUtil_multipleInternalDisplays() throws RemoteException {
+ // Default display
+ var defaultDisplayInfo = createDisplayInfoForDisplay(Display.DEFAULT_DISPLAY,
+ Display.TYPE_INTERNAL, 2000, 2000, 390);
+ var defaultDisplay = new Display(mDisplayManagerGlobal, defaultDisplayInfo.displayId,
+ defaultDisplayInfo,
+ (DisplayAdjustments) null);
+ doReturn(defaultDisplay).when(mDisplayManager).getDisplay(defaultDisplayInfo.displayId);
+
+ // Create another internal display
+ var internalDisplayInfo = createDisplayInfoForDisplay(1, Display.TYPE_INTERNAL,
+ 2000, 1000, 390);
+ var internalDisplay = new Display(mDisplayManagerGlobal, internalDisplayInfo.displayId,
+ internalDisplayInfo,
+ (DisplayAdjustments) null);
+ doReturn(internalDisplay).when(mDisplayManager).getDisplay(internalDisplayInfo.displayId);
+
+ doReturn(new Display[]{defaultDisplay, internalDisplay}).when(mDisplayManager).getDisplays(
+ anyString());
+
+ mDisplayDensityUtils = new DisplayDensityUtils(mContext);
+
+ assertThat(mDisplayDensityUtils.getValues()).isEqualTo(new int[]{330, 390, 426, 462, 500});
+ }
+
+ private DisplayInfo createDisplayInfoForDisplay(int displayId, int displayType,
+ int width, int height, int density) throws RemoteException {
+ var displayInfo = new DisplayInfo();
+ displayInfo.displayId = displayId;
+ displayInfo.type = displayType;
+ displayInfo.logicalWidth = width;
+ displayInfo.logicalHeight = height;
+ displayInfo.logicalDensityDpi = density;
+
+ doReturn(displayInfo).when(mDisplayManagerGlobal).getDisplayInfo(displayInfo.displayId);
+ doReturn(displayInfo.logicalDensityDpi).when(mIWindowManager).getInitialDisplayDensity(
+ displayInfo.displayId);
+ return displayInfo;
+ }
+}
+
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 418a7a5..835dd7a 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
@@ -76,7 +76,7 @@
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.selection.selectable
import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.foundation.text.AutoSize
+import androidx.compose.foundation.text.TextAutoSize
import androidx.compose.foundation.text.BasicText
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
@@ -1045,7 +1045,7 @@
color = colors.onPrimary,
textAlign = TextAlign.Center,
),
- autoSize = AutoSize.StepBased(maxFontSize = 36.sp, stepSize = 0.1.sp),
+ autoSize = TextAutoSize.StepBased(maxFontSize = 36.sp, stepSize = 0.1.sp),
modifier =
Modifier.focusable().semantics(mergeDescendants = true) {
contentDescription = titleForEmptyStateCTA
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
index 16002bc..8ad96a5 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
@@ -57,7 +57,8 @@
internal constructor(
communalContent: List<CommunalContentModel>,
private val onAddWidget: (componentName: ComponentName, user: UserHandle, rank: Int) -> Unit,
- private val onDeleteWidget: (id: Int, componentName: ComponentName, rank: Int) -> Unit,
+ private val onDeleteWidget:
+ (id: Int, key: String, componentName: ComponentName, rank: Int) -> Unit,
private val onReorderWidgets: (widgetIdToRankMap: Map<Int, Int>) -> Unit,
private val onResizeWidget:
(
@@ -81,7 +82,7 @@
if (list[indexToRemove].isWidgetContent()) {
val widget = list[indexToRemove] as CommunalContentModel.WidgetContent
list.apply { removeAt(indexToRemove) }
- onDeleteWidget(widget.appWidgetId, widget.componentName, widget.rank)
+ onDeleteWidget(widget.appWidgetId, widget.key, widget.componentName, widget.rank)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index aa0d474..0c502e6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -109,6 +109,13 @@
rememberMutableSceneTransitionLayoutState(
initialScene = initialSceneKey,
canChangeScene = { toScene -> viewModel.canChangeScene(toScene) },
+ canShowOverlay = { overlay -> viewModel.canShowOrReplaceOverlay(overlay) },
+ canReplaceOverlay = { beingReplaced, newlyShown ->
+ viewModel.canShowOrReplaceOverlay(
+ newlyShown = newlyShown,
+ beingReplaced = beingReplaced,
+ )
+ },
transitions = sceneTransitions,
onTransitionStart = { transition ->
sceneJankMonitor.onTransitionStart(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
index 6f2082b..7051f81 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
@@ -24,53 +24,47 @@
import androidx.test.filters.SmallTest
import com.android.internal.logging.uiEventLoggerFake
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
-import com.android.systemui.Flags.FLAG_COMMUNAL_SCENE_KTF_REFACTOR
+import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.communal.domain.interactor.setCommunalAvailable
+import com.android.systemui.communal.domain.interactor.setCommunalV2ConfigEnabled
import com.android.systemui.communal.shared.log.CommunalUiEvent
import com.android.systemui.communal.shared.model.CommunalScenes
-import com.android.systemui.communal.shared.model.EditModeState
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.dock.dockManager
-import com.android.systemui.dock.fakeDockManager
import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
import com.android.systemui.flags.andSceneContainer
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.notificationShadeWindowController
-import com.android.systemui.statusbar.phone.centralSurfaces
-import com.android.systemui.statusbar.phone.centralSurfacesOptional
import com.android.systemui.testKosmos
import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
+import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.advanceTimeBy
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
import org.junit.Before
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.kotlin.whenever
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
@EnableFlags(FLAG_COMMUNAL_HUB)
@@ -82,7 +76,8 @@
@JvmStatic
@Parameters(name = "{0}")
fun getParams(): List<FlagsParameterization> {
- return FlagsParameterization.allCombinationsOf().andSceneContainer()
+ return FlagsParameterization.allCombinationsOf(FLAG_GLANCEABLE_HUB_V2)
+ .andSceneContainer()
}
}
@@ -90,9 +85,22 @@
mSetFlagsRule.setFlagsParameterization(flags)
}
- private val kosmos = testKosmos()
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
- private lateinit var underTest: CommunalSceneStartable
+ private val Kosmos.underTest by
+ Kosmos.Fixture {
+ CommunalSceneStartable(
+ communalInteractor = communalInteractor,
+ communalSettingsInteractor = communalSettingsInteractor,
+ communalSceneInteractor = communalSceneInteractor,
+ keyguardInteractor = keyguardInteractor,
+ systemSettings = fakeSettings,
+ notificationShadeWindowController = notificationShadeWindowController,
+ bgScope = applicationCoroutineScope,
+ mainDispatcher = testDispatcher,
+ uiEventLogger = uiEventLoggerFake,
+ )
+ }
@Before
fun setUp() {
@@ -102,646 +110,314 @@
SCREEN_TIMEOUT,
UserHandle.USER_CURRENT,
)
- kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
+ fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
- underTest =
- CommunalSceneStartable(
- dockManager = dockManager,
- communalInteractor = communalInteractor,
- communalSettingsInteractor = communalSettingsInteractor,
- communalSceneInteractor = communalSceneInteractor,
- keyguardTransitionInteractor = keyguardTransitionInteractor,
- keyguardInteractor = keyguardInteractor,
- systemSettings = fakeSettings,
- notificationShadeWindowController = notificationShadeWindowController,
- applicationScope = applicationCoroutineScope,
- bgScope = applicationCoroutineScope,
- mainDispatcher = testDispatcher,
- centralSurfacesOpt = centralSurfacesOptional,
- uiEventLogger = uiEventLoggerFake,
- )
- .apply { start() }
+ underTest.start()
// Make communal available so that communalInteractor.desiredScene accurately reflects
// scene changes instead of just returning Blank.
- with(kosmos.testScope) {
- launch { setCommunalAvailable(true) }
- testScheduler.runCurrent()
- }
+ runBlocking { setCommunalAvailable(true) }
+ setCommunalV2ConfigEnabled(true)
}
}
@Test
- @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR, FLAG_SCENE_CONTAINER)
- fun keyguardGoesAway_whenLaunchingEditMode_doNotForceBlankScene() =
- with(kosmos) {
- testScope.runTest {
- val scene by collectLastValue(communalSceneInteractor.currentScene)
-
- communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
- assertThat(scene).isEqualTo(CommunalScenes.Communal)
-
- communalSceneInteractor.setEditModeState(EditModeState.STARTING)
- fakeKeyguardTransitionRepository.sendTransitionSteps(
- from = KeyguardState.PRIMARY_BOUNCER,
- to = KeyguardState.GONE,
- testScope = this,
- )
-
- assertThat(scene).isEqualTo(CommunalScenes.Communal)
- }
- }
-
- @Test
- @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR, FLAG_SCENE_CONTAINER)
- fun keyguardGoesAway_whenLaunchingWidget_doNotForceBlankScene() =
- with(kosmos) {
- testScope.runTest {
- val scene by collectLastValue(communalSceneInteractor.currentScene)
-
- communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
- assertThat(scene).isEqualTo(CommunalScenes.Communal)
-
- communalSceneInteractor.setIsLaunchingWidget(true)
- fakeKeyguardTransitionRepository.sendTransitionSteps(
- from = KeyguardState.PRIMARY_BOUNCER,
- to = KeyguardState.GONE,
- testScope = this,
- )
-
- assertThat(scene).isEqualTo(CommunalScenes.Communal)
- }
- }
-
- @Test
- @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR, FLAG_SCENE_CONTAINER)
- fun keyguardGoesAway_whenNotLaunchingWidget_forceBlankScene() =
- with(kosmos) {
- testScope.runTest {
- val scene by collectLastValue(communalSceneInteractor.currentScene)
-
- communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
- assertThat(scene).isEqualTo(CommunalScenes.Communal)
-
- communalSceneInteractor.setIsLaunchingWidget(false)
- fakeKeyguardTransitionRepository.sendTransitionSteps(
- from = KeyguardState.PRIMARY_BOUNCER,
- to = KeyguardState.GONE,
- testScope = this,
- )
-
- assertThat(scene).isEqualTo(CommunalScenes.Blank)
- }
- }
-
- @Test
- @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR, FLAG_SCENE_CONTAINER)
- fun keyguardGoesAway_whenInEditMode_doesNotChangeScene() =
- with(kosmos) {
- testScope.runTest {
- val scene by collectLastValue(communalSceneInteractor.currentScene)
- communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
- assertThat(scene).isEqualTo(CommunalScenes.Communal)
-
- communalInteractor.setEditModeOpen(true)
- fakeKeyguardTransitionRepository.sendTransitionSteps(
- from = KeyguardState.ALTERNATE_BOUNCER,
- to = KeyguardState.GONE,
- testScope = this,
- )
- // Scene change will be handled in EditWidgetsActivity not here
- assertThat(scene).isEqualTo(CommunalScenes.Communal)
- }
- }
-
- @Ignore("Ignored until custom animations are implemented in b/322787129")
- @Test
- @DisableFlags(FLAG_SCENE_CONTAINER)
- fun deviceDocked_forceCommunalScene() =
- with(kosmos) {
- testScope.runTest {
- val scene by collectLastValue(communalSceneInteractor.currentScene)
- assertThat(scene).isEqualTo(CommunalScenes.Blank)
-
- updateDocked(true)
- fakeKeyguardTransitionRepository.sendTransitionSteps(
- from = KeyguardState.GONE,
- to = KeyguardState.LOCKSCREEN,
- testScope = this,
- )
- assertThat(scene).isEqualTo(CommunalScenes.Communal)
- }
- }
-
- @Test
- @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR, FLAG_SCENE_CONTAINER)
- fun occluded_forceBlankScene() =
- with(kosmos) {
- testScope.runTest {
- whenever(centralSurfaces.isLaunchingActivityOverLockscreen).thenReturn(false)
- val scene by collectLastValue(communalSceneInteractor.currentScene)
- communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
- assertThat(scene).isEqualTo(CommunalScenes.Communal)
-
- updateDocked(true)
- fakeKeyguardTransitionRepository.sendTransitionSteps(
- from = KeyguardState.GLANCEABLE_HUB,
- to = KeyguardState.OCCLUDED,
- testScope = this,
- )
- assertThat(scene).isEqualTo(CommunalScenes.Blank)
- }
- }
-
- @Test
- @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR, FLAG_SCENE_CONTAINER)
- fun occluded_doesNotForceBlankSceneIfLaunchingActivityOverLockscreen() =
- with(kosmos) {
- testScope.runTest {
- whenever(centralSurfaces.isLaunchingActivityOverLockscreen).thenReturn(true)
- val scene by collectLastValue(communalSceneInteractor.currentScene)
- communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
- assertThat(scene).isEqualTo(CommunalScenes.Communal)
-
- updateDocked(true)
- fakeKeyguardTransitionRepository.sendTransitionSteps(
- from = KeyguardState.GLANCEABLE_HUB,
- to = KeyguardState.OCCLUDED,
- testScope = this,
- )
- assertThat(scene).isEqualTo(CommunalScenes.Communal)
- }
- }
-
- @Test
- @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR, FLAG_SCENE_CONTAINER)
- fun deviceDocked_doesNotForceCommunalIfTransitioningFromCommunal() =
- with(kosmos) {
- testScope.runTest {
- val scene by collectLastValue(communalSceneInteractor.currentScene)
- assertThat(scene).isEqualTo(CommunalScenes.Blank)
-
- updateDocked(true)
- fakeKeyguardTransitionRepository.sendTransitionSteps(
- from = KeyguardState.GLANCEABLE_HUB,
- to = KeyguardState.LOCKSCREEN,
- testScope = this,
- )
- assertThat(scene).isEqualTo(CommunalScenes.Blank)
- }
- }
-
- @Test
- @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR, FLAG_SCENE_CONTAINER)
- fun deviceAsleep_forceBlankSceneAfterTimeout() =
- with(kosmos) {
- testScope.runTest {
- val scene by collectLastValue(communalSceneInteractor.currentScene)
- communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
- assertThat(scene).isEqualTo(CommunalScenes.Communal)
-
- fakeKeyguardTransitionRepository.sendTransitionSteps(
- from = KeyguardState.GLANCEABLE_HUB,
- to = KeyguardState.OFF,
- testScope = this,
- )
- assertThat(scene).isEqualTo(CommunalScenes.Communal)
-
- advanceTimeBy(CommunalSceneStartable.AWAKE_DEBOUNCE_DELAY)
-
- assertThat(scene).isEqualTo(CommunalScenes.Blank)
- }
- }
-
- @Test
- @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR, FLAG_SCENE_CONTAINER)
- fun deviceAsleep_wakesUpBeforeTimeout_noChangeInScene() =
- with(kosmos) {
- testScope.runTest {
- val scene by collectLastValue(communalSceneInteractor.currentScene)
- communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
- assertThat(scene).isEqualTo(CommunalScenes.Communal)
-
- fakeKeyguardTransitionRepository.sendTransitionSteps(
- from = KeyguardState.GLANCEABLE_HUB,
- to = KeyguardState.OFF,
- testScope = this,
- )
- assertThat(scene).isEqualTo(CommunalScenes.Communal)
- advanceTimeBy(CommunalSceneStartable.AWAKE_DEBOUNCE_DELAY / 2)
- assertThat(scene).isEqualTo(CommunalScenes.Communal)
-
- fakeKeyguardTransitionRepository.sendTransitionSteps(
- from = KeyguardState.OFF,
- to = KeyguardState.GLANCEABLE_HUB,
- testScope = this,
- )
-
- advanceTimeBy(CommunalSceneStartable.AWAKE_DEBOUNCE_DELAY)
- assertThat(scene).isEqualTo(CommunalScenes.Communal)
- }
- }
-
- @Ignore("Ignored until custom animations are implemented in b/322787129")
- @Test
- @DisableFlags(FLAG_SCENE_CONTAINER)
- fun dockingOnLockscreen_forcesCommunal() =
- with(kosmos) {
- testScope.runTest {
- communalSceneInteractor.changeScene(CommunalScenes.Blank, "test")
- val scene by collectLastValue(communalSceneInteractor.currentScene)
-
- // device is docked while on the lockscreen
- fakeKeyguardTransitionRepository.sendTransitionSteps(
- from = KeyguardState.GLANCEABLE_HUB,
- to = KeyguardState.LOCKSCREEN,
- testScope = this,
- )
- updateDocked(true)
-
- assertThat(scene).isEqualTo(CommunalScenes.Blank)
- advanceTimeBy(CommunalSceneStartable.DOCK_DEBOUNCE_DELAY)
- assertThat(scene).isEqualTo(CommunalScenes.Communal)
- }
- }
-
- @Ignore("Ignored until custom animations are implemented in b/322787129")
- @Test
- @DisableFlags(FLAG_SCENE_CONTAINER)
- fun dockingOnLockscreen_doesNotForceCommunalIfDreamStarts() =
- with(kosmos) {
- testScope.runTest {
- communalSceneInteractor.changeScene(CommunalScenes.Blank, "test")
- val scene by collectLastValue(communalSceneInteractor.currentScene)
-
- // device is docked while on the lockscreen
- fakeKeyguardTransitionRepository.sendTransitionSteps(
- from = KeyguardState.GLANCEABLE_HUB,
- to = KeyguardState.LOCKSCREEN,
- testScope = this,
- )
- updateDocked(true)
-
- assertThat(scene).isEqualTo(CommunalScenes.Blank)
- advanceTimeBy(CommunalSceneStartable.DOCK_DEBOUNCE_DELAY / 2)
- assertThat(scene).isEqualTo(CommunalScenes.Blank)
-
- // dream starts shortly after docking
- fakeKeyguardTransitionRepository.sendTransitionSteps(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.DREAMING,
- testScope = this,
- )
- advanceTimeBy(CommunalSceneStartable.DOCK_DEBOUNCE_DELAY)
- assertThat(scene).isEqualTo(CommunalScenes.Blank)
- }
- }
-
- @Test
- @DisableFlags(FLAG_SCENE_CONTAINER)
+ @DisableFlags(FLAG_SCENE_CONTAINER, FLAG_GLANCEABLE_HUB_V2)
fun hubTimeout_whenDreaming_goesToBlank() =
- with(kosmos) {
- testScope.runTest {
- // Device is dreaming and on communal.
- updateDreaming(true)
- communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
+ kosmos.runTest {
+ // Device is dreaming and on communal.
+ fakeKeyguardRepository.setDreaming(true)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
- val scene by collectLastValue(communalSceneInteractor.currentScene)
- assertThat(scene).isEqualTo(CommunalScenes.Communal)
+ val scene by collectLastValue(communalSceneInteractor.currentScene)
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
- // Scene times out back to blank after the screen timeout.
- advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
- assertThat(scene).isEqualTo(CommunalScenes.Blank)
- }
+ // Scene times out back to blank after the screen timeout.
+ advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
+ assertThat(scene).isEqualTo(CommunalScenes.Blank)
}
@Test
- @DisableFlags(FLAG_SCENE_CONTAINER)
+ @DisableFlags(FLAG_SCENE_CONTAINER, FLAG_GLANCEABLE_HUB_V2)
fun hubTimeout_notDreaming_staysOnCommunal() =
- with(kosmos) {
- testScope.runTest {
- // Device is not dreaming and on communal.
- updateDreaming(false)
- communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
+ kosmos.runTest {
+ // Device is not dreaming and on communal.
+ fakeKeyguardRepository.setDreaming(false)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
- // Scene stays as Communal
- advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
- val scene by collectLastValue(communalSceneInteractor.currentScene)
- assertThat(scene).isEqualTo(CommunalScenes.Communal)
- }
+ // Scene stays as Communal
+ advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
+ val scene by collectLastValue(communalSceneInteractor.currentScene)
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
}
@Test
- @DisableFlags(FLAG_SCENE_CONTAINER)
+ @DisableFlags(FLAG_SCENE_CONTAINER, FLAG_GLANCEABLE_HUB_V2)
fun hubTimeout_dreamStopped_staysOnCommunal() =
- with(kosmos) {
- testScope.runTest {
- // Device is dreaming and on communal.
- updateDreaming(true)
- communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
+ kosmos.runTest {
+ // Device is dreaming and on communal.
+ fakeKeyguardRepository.setDreaming(true)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
- val scene by collectLastValue(communalSceneInteractor.currentScene)
- assertThat(scene).isEqualTo(CommunalScenes.Communal)
+ val scene by collectLastValue(communalSceneInteractor.currentScene)
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
- // Wait a bit, but not long enough to timeout.
- advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
- assertThat(scene).isEqualTo(CommunalScenes.Communal)
+ // Wait a bit, but not long enough to timeout.
+ advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
- // Dream stops, timeout is cancelled and device stays on hub, because the regular
- // screen timeout will take effect at this point.
- updateDreaming(false)
- advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
- assertThat(scene).isEqualTo(CommunalScenes.Communal)
- }
+ // Dream stops, timeout is cancelled and device stays on hub, because the regular
+ // screen timeout will take effect at this point.
+ fakeKeyguardRepository.setDreaming(false)
+ advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
}
@Test
- @DisableFlags(FLAG_SCENE_CONTAINER)
+ @DisableFlags(FLAG_SCENE_CONTAINER, FLAG_GLANCEABLE_HUB_V2)
fun hubTimeout_dreamStartedHalfway_goesToCommunal() =
- with(kosmos) {
- testScope.runTest {
- // Device is on communal, but not dreaming.
- updateDreaming(false)
- communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
+ kosmos.runTest {
+ // Device is on communal, but not dreaming.
+ fakeKeyguardRepository.setDreaming(false)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
- val scene by collectLastValue(communalSceneInteractor.currentScene)
- assertThat(scene).isEqualTo(CommunalScenes.Communal)
+ val scene by collectLastValue(communalSceneInteractor.currentScene)
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
- // Wait a bit, but not long enough to timeout, then start dreaming.
- advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
- updateDreaming(true)
- assertThat(scene).isEqualTo(CommunalScenes.Communal)
+ // Wait a bit, but not long enough to timeout, then start dreaming.
+ advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
+ fakeKeyguardRepository.setDreaming(true)
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
- // Device times out after one screen timeout interval, dream doesn't reset timeout.
- advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
- assertThat(scene).isEqualTo(CommunalScenes.Blank)
- }
+ // Device times out after one screen timeout interval, dream doesn't reset timeout.
+ advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
+ assertThat(scene).isEqualTo(CommunalScenes.Blank)
}
@Test
- @DisableFlags(FLAG_SCENE_CONTAINER)
+ @DisableFlags(FLAG_SCENE_CONTAINER, FLAG_GLANCEABLE_HUB_V2)
fun hubTimeout_dreamAfterInitialTimeout_goesToBlank() =
- with(kosmos) {
- testScope.runTest {
- // Device is on communal.
- communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
+ kosmos.runTest {
+ // Device is on communal.
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
- // Device stays on the hub after the timeout since we're not dreaming.
- advanceTimeBy(SCREEN_TIMEOUT.milliseconds * 2)
- val scene by collectLastValue(communalSceneInteractor.currentScene)
- assertThat(scene).isEqualTo(CommunalScenes.Communal)
+ // Device stays on the hub after the timeout since we're not dreaming.
+ testScope.advanceTimeBy(SCREEN_TIMEOUT.milliseconds * 2)
+ val scene by collectLastValue(communalSceneInteractor.currentScene)
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
- // Start dreaming.
- updateDreaming(true)
- advanceTimeBy(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS)
+ // Start dreaming.
+ fakeKeyguardRepository.setDreaming(true)
+ advanceTimeBy(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS.milliseconds)
- // Hub times out immediately.
- assertThat(scene).isEqualTo(CommunalScenes.Blank)
- }
+ // Hub times out immediately.
+ assertThat(scene).isEqualTo(CommunalScenes.Blank)
}
@Test
- @DisableFlags(FLAG_SCENE_CONTAINER)
+ @DisableFlags(FLAG_SCENE_CONTAINER, FLAG_GLANCEABLE_HUB_V2)
fun hubTimeout_userActivityTriggered_resetsTimeout() =
- with(kosmos) {
- testScope.runTest {
- // Device is dreaming and on communal.
- updateDreaming(true)
- communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
+ kosmos.runTest {
+ // Device is dreaming and on communal.
+ fakeKeyguardRepository.setDreaming(true)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
- val scene by collectLastValue(communalSceneInteractor.currentScene)
- assertThat(scene).isEqualTo(CommunalScenes.Communal)
+ val scene by collectLastValue(communalSceneInteractor.currentScene)
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
- // Wait a bit, but not long enough to timeout.
- advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
+ // Wait a bit, but not long enough to timeout.
+ advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
- // Send user interaction to reset timeout.
- communalInteractor.signalUserInteraction()
+ // Send user interaction to reset timeout.
+ communalInteractor.signalUserInteraction()
- // If user activity didn't reset timeout, we would have gone back to Blank by now.
- advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
- assertThat(scene).isEqualTo(CommunalScenes.Communal)
+ // If user activity didn't reset timeout, we would have gone back to Blank by now.
+ advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
- // Timeout happens one interval after the user interaction.
- advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
- assertThat(scene).isEqualTo(CommunalScenes.Blank)
- }
+ // Timeout happens one interval after the user interaction.
+ advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
+ assertThat(scene).isEqualTo(CommunalScenes.Blank)
}
@Test
- @DisableFlags(FLAG_SCENE_CONTAINER)
+ @DisableFlags(FLAG_SCENE_CONTAINER, FLAG_GLANCEABLE_HUB_V2)
fun hubTimeout_screenTimeoutChanged() =
- with(kosmos) {
- testScope.runTest {
- fakeSettings.putInt(Settings.System.SCREEN_OFF_TIMEOUT, SCREEN_TIMEOUT * 2)
+ kosmos.runTest {
+ fakeSettings.putInt(Settings.System.SCREEN_OFF_TIMEOUT, SCREEN_TIMEOUT * 2)
- // Device is dreaming and on communal.
- updateDreaming(true)
- communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
+ // Device is dreaming and on communal.
+ fakeKeyguardRepository.setDreaming(true)
+ communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
- val scene by collectLastValue(communalSceneInteractor.currentScene)
- assertThat(scene).isEqualTo(CommunalScenes.Communal)
+ val scene by collectLastValue(communalSceneInteractor.currentScene)
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
- // Scene times out back to blank after the screen timeout.
- advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
- assertThat(scene).isEqualTo(CommunalScenes.Communal)
+ // Scene times out back to blank after the screen timeout.
+ advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
- advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
- assertThat(scene).isEqualTo(CommunalScenes.Blank)
- assertThat(uiEventLoggerFake.logs.first().eventId)
- .isEqualTo(CommunalUiEvent.COMMUNAL_HUB_TIMEOUT.id)
- assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
- }
+ advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
+ assertThat(scene).isEqualTo(CommunalScenes.Blank)
+ assertThat(uiEventLoggerFake.logs.first().eventId)
+ .isEqualTo(CommunalUiEvent.COMMUNAL_HUB_TIMEOUT.id)
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
}
@Test
@EnableFlags(FLAG_SCENE_CONTAINER)
+ @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
fun hubTimeout_withSceneContainer_whenDreaming_goesToBlank() =
- with(kosmos) {
- testScope.runTest {
- // Device is dreaming and on communal.
- updateDreaming(true)
- sceneInteractor.changeScene(Scenes.Communal, "test")
+ kosmos.runTest {
+ // Device is dreaming and on communal.
+ fakeKeyguardRepository.setDreaming(true)
+ sceneInteractor.changeScene(Scenes.Communal, "test")
- val scene by collectLastValue(sceneInteractor.currentScene)
- assertThat(scene).isEqualTo(Scenes.Communal)
+ val scene by collectLastValue(sceneInteractor.currentScene)
+ assertThat(scene).isEqualTo(Scenes.Communal)
- // Scene times out back to blank after the screen timeout.
- advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
- assertThat(scene).isEqualTo(Scenes.Dream)
- }
+ // Scene times out back to blank after the screen timeout.
+ advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
+ assertThat(scene).isEqualTo(Scenes.Dream)
}
@Test
@EnableFlags(FLAG_SCENE_CONTAINER)
+ @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
fun hubTimeout_withSceneContainer_notDreaming_staysOnCommunal() =
- with(kosmos) {
- testScope.runTest {
- // Device is not dreaming and on communal.
- updateDreaming(false)
- sceneInteractor.changeScene(Scenes.Communal, "test")
+ kosmos.runTest {
+ // Device is not dreaming and on communal.
+ fakeKeyguardRepository.setDreaming(false)
+ sceneInteractor.changeScene(Scenes.Communal, "test")
- // Scene stays as Communal
- advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
- val scene by collectLastValue(sceneInteractor.currentScene)
- assertThat(scene).isEqualTo(Scenes.Communal)
- }
+ // Scene stays as Communal
+ advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
+ val scene by collectLastValue(sceneInteractor.currentScene)
+ assertThat(scene).isEqualTo(Scenes.Communal)
}
@Test
@EnableFlags(FLAG_SCENE_CONTAINER)
+ @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
fun hubTimeout_withSceneContainer_dreamStopped_staysOnCommunal() =
- with(kosmos) {
- testScope.runTest {
- // Device is dreaming and on communal.
- updateDreaming(true)
- sceneInteractor.changeScene(Scenes.Communal, "test")
+ kosmos.runTest {
+ // Device is dreaming and on communal.
+ fakeKeyguardRepository.setDreaming(true)
+ sceneInteractor.changeScene(Scenes.Communal, "test")
- val scene by collectLastValue(sceneInteractor.currentScene)
- assertThat(scene).isEqualTo(Scenes.Communal)
+ val scene by collectLastValue(sceneInteractor.currentScene)
+ assertThat(scene).isEqualTo(Scenes.Communal)
- // Wait a bit, but not long enough to timeout.
- advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
- assertThat(scene).isEqualTo(Scenes.Communal)
+ // Wait a bit, but not long enough to timeout.
+ advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
+ assertThat(scene).isEqualTo(Scenes.Communal)
- // Dream stops, timeout is cancelled and device stays on hub, because the regular
- // screen timeout will take effect at this point.
- updateDreaming(false)
- advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
- assertThat(scene).isEqualTo(Scenes.Communal)
- }
+ // Dream stops, timeout is cancelled and device stays on hub, because the regular
+ // screen timeout will take effect at this point.
+ fakeKeyguardRepository.setDreaming(false)
+ advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
+ assertThat(scene).isEqualTo(Scenes.Communal)
}
@Test
@EnableFlags(FLAG_SCENE_CONTAINER)
+ @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
fun hubTimeout_withSceneContainer_dreamStartedHalfway_goesToCommunal() =
- with(kosmos) {
- testScope.runTest {
- // Device is on communal, but not dreaming.
- updateDreaming(false)
- sceneInteractor.changeScene(Scenes.Communal, "test")
+ kosmos.runTest {
+ // Device is on communal, but not dreaming.
+ fakeKeyguardRepository.setDreaming(false)
+ sceneInteractor.changeScene(Scenes.Communal, "test")
- val scene by collectLastValue(sceneInteractor.currentScene)
- assertThat(scene).isEqualTo(Scenes.Communal)
+ val scene by collectLastValue(sceneInteractor.currentScene)
+ assertThat(scene).isEqualTo(Scenes.Communal)
- // Wait a bit, but not long enough to timeout, then start dreaming.
- advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
- updateDreaming(true)
- assertThat(scene).isEqualTo(Scenes.Communal)
+ // Wait a bit, but not long enough to timeout, then start dreaming.
+ advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
+ fakeKeyguardRepository.setDreaming(true)
+ assertThat(scene).isEqualTo(Scenes.Communal)
- // Device times out after one screen timeout interval, dream doesn't reset timeout.
- advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
- assertThat(scene).isEqualTo(Scenes.Dream)
- }
+ // Device times out after one screen timeout interval, dream doesn't reset timeout.
+ advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
+ assertThat(scene).isEqualTo(Scenes.Dream)
}
@Test
@EnableFlags(FLAG_SCENE_CONTAINER)
+ @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
fun hubTimeout_withSceneContainer_dreamAfterInitialTimeout_goesToBlank() =
- with(kosmos) {
- testScope.runTest {
- // Device is on communal.
- sceneInteractor.changeScene(Scenes.Communal, "test")
+ kosmos.runTest {
+ // Device is on communal.
+ sceneInteractor.changeScene(Scenes.Communal, "test")
- // Device stays on the hub after the timeout since we're not dreaming.
- advanceTimeBy(SCREEN_TIMEOUT.milliseconds * 2)
- val scene by collectLastValue(sceneInteractor.currentScene)
- assertThat(scene).isEqualTo(Scenes.Communal)
+ // Device stays on the hub after the timeout since we're not dreaming.
+ advanceTimeBy(SCREEN_TIMEOUT.milliseconds * 2)
+ val scene by collectLastValue(sceneInteractor.currentScene)
+ assertThat(scene).isEqualTo(Scenes.Communal)
- // Start dreaming.
- updateDreaming(true)
- advanceTimeBy(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS)
+ // Start dreaming.
+ fakeKeyguardRepository.setDreaming(true)
+ advanceTimeBy(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS.milliseconds)
- // Hub times out immediately.
- assertThat(scene).isEqualTo(Scenes.Dream)
- }
+ // Hub times out immediately.
+ assertThat(scene).isEqualTo(Scenes.Dream)
}
@Test
@EnableFlags(FLAG_SCENE_CONTAINER)
+ @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
fun hubTimeout_withSceneContainer_userActivityTriggered_resetsTimeout() =
- with(kosmos) {
- testScope.runTest {
- // Device is dreaming and on communal.
- updateDreaming(true)
- sceneInteractor.changeScene(Scenes.Communal, "test")
+ kosmos.runTest {
+ // Device is dreaming and on communal.
+ fakeKeyguardRepository.setDreaming(true)
+ sceneInteractor.changeScene(Scenes.Communal, "test")
- val scene by collectLastValue(sceneInteractor.currentScene)
- assertThat(scene).isEqualTo(Scenes.Communal)
+ val scene by collectLastValue(sceneInteractor.currentScene)
+ assertThat(scene).isEqualTo(Scenes.Communal)
- // Wait a bit, but not long enough to timeout.
- advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
+ // Wait a bit, but not long enough to timeout.
+ advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
- // Send user interaction to reset timeout.
- communalInteractor.signalUserInteraction()
+ // Send user interaction to reset timeout.
+ communalInteractor.signalUserInteraction()
- // If user activity didn't reset timeout, we would have gone back to Blank by now.
- advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
- assertThat(scene).isEqualTo(Scenes.Communal)
+ // If user activity didn't reset timeout, we would have gone back to Blank by now.
+ advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
+ assertThat(scene).isEqualTo(Scenes.Communal)
- // Timeout happens one interval after the user interaction.
- advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
- assertThat(scene).isEqualTo(Scenes.Dream)
- }
+ // Timeout happens one interval after the user interaction.
+ advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
+ assertThat(scene).isEqualTo(Scenes.Dream)
}
@Test
@EnableFlags(FLAG_SCENE_CONTAINER)
+ @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
fun hubTimeout_withSceneContainer_screenTimeoutChanged() =
- with(kosmos) {
- testScope.runTest {
- fakeSettings.putInt(Settings.System.SCREEN_OFF_TIMEOUT, SCREEN_TIMEOUT * 2)
+ kosmos.runTest {
+ fakeSettings.putInt(Settings.System.SCREEN_OFF_TIMEOUT, SCREEN_TIMEOUT * 2)
- // Device is dreaming and on communal.
- updateDreaming(true)
- sceneInteractor.changeScene(Scenes.Communal, "test")
+ // Device is dreaming and on communal.
+ fakeKeyguardRepository.setDreaming(true)
+ sceneInteractor.changeScene(Scenes.Communal, "test")
- val scene by collectLastValue(sceneInteractor.currentScene)
- assertThat(scene).isEqualTo(Scenes.Communal)
+ val scene by collectLastValue(sceneInteractor.currentScene)
+ assertThat(scene).isEqualTo(Scenes.Communal)
- // Scene times out back to blank after the screen timeout.
- advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
- assertThat(scene).isEqualTo(Scenes.Communal)
+ // Scene times out back to blank after the screen timeout.
+ advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
+ assertThat(scene).isEqualTo(Scenes.Communal)
- advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
- assertThat(scene).isEqualTo(Scenes.Dream)
- assertThat(uiEventLoggerFake.logs.first().eventId)
- .isEqualTo(CommunalUiEvent.COMMUNAL_HUB_TIMEOUT.id)
- assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
- }
+ advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
+ assertThat(scene).isEqualTo(Scenes.Dream)
+ assertThat(uiEventLoggerFake.logs.first().eventId)
+ .isEqualTo(CommunalUiEvent.COMMUNAL_HUB_TIMEOUT.id)
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
}
- @Test
- @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR, FLAG_SCENE_CONTAINER)
- fun transitionFromDozingToGlanceableHub_forcesCommunal() =
- with(kosmos) {
- testScope.runTest {
- val scene by collectLastValue(communalSceneInteractor.currentScene)
- communalSceneInteractor.changeScene(CommunalScenes.Blank, "test")
- assertThat(scene).isEqualTo(CommunalScenes.Blank)
-
- fakeKeyguardTransitionRepository.sendTransitionSteps(
- from = KeyguardState.DOZING,
- to = KeyguardState.GLANCEABLE_HUB,
- testScope = this,
- )
-
- assertThat(scene).isEqualTo(CommunalScenes.Communal)
- }
- }
-
- private fun TestScope.updateDocked(docked: Boolean) =
- with(kosmos) {
- runCurrent()
- fakeDockManager.setIsDocked(docked)
- // TODO(b/322787129): uncomment once custom animations are in place
- // fakeDockManager.setDockEvent(DockManager.STATE_DOCKED)
- runCurrent()
- }
-
- private fun TestScope.updateDreaming(dreaming: Boolean) =
- with(kosmos) {
- fakeKeyguardRepository.setDreaming(dreaming)
- runCurrent()
- }
+ /**
+ * Advances time by duration + 1 millisecond, to ensure that tasks scheduled to run at
+ * currentTime + duration are scheduled.
+ */
+ private fun Kosmos.advanceTimeBy(duration: Duration) =
+ testScope.advanceTimeBy(duration + 1.milliseconds)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractorTest.kt
index ff722bf..aa96073 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractorTest.kt
@@ -22,7 +22,6 @@
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.ObservableTransitionState.Idle
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
-import com.android.systemui.Flags.FLAG_COMMUNAL_SCENE_KTF_REFACTOR
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.repository.communalSceneRepository
import com.android.systemui.communal.shared.model.CommunalScenes
@@ -64,7 +63,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
-@EnableFlags(FLAG_COMMUNAL_HUB, FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
+@EnableFlags(FLAG_COMMUNAL_HUB)
@DisableSceneContainer
class CommunalSceneTransitionInteractorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
index 522650b..f5f5dd8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
@@ -201,6 +201,7 @@
underTest.onDeleteWidget(
id = 0,
+ key = "key_0",
componentName = ComponentName("test_package", "test_class"),
rank = 30,
)
@@ -214,6 +215,24 @@
}
@Test
+ fun deleteWidget_clearsSelectedKey() =
+ kosmos.runTest {
+ val selectedKey by collectLastValue(underTest.selectedKey)
+ underTest.setSelectedKey("test_key")
+ assertThat(selectedKey).isEqualTo("test_key")
+
+ // Selected key is deleted.
+ underTest.onDeleteWidget(
+ id = 0,
+ key = "test_key",
+ componentName = ComponentName("test_package", "test_class"),
+ rank = 30,
+ )
+
+ assertThat(selectedKey).isNull()
+ }
+
+ @Test
fun reorderWidget_uiEventLogging_start() =
kosmos.runTest {
underTest.onReorderWidgetStart(CommunalContentModel.KEY.widget(123))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
index 7e93f5a..9be786f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
@@ -25,7 +25,6 @@
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
-import com.android.systemui.Flags.FLAG_COMMUNAL_SCENE_KTF_REFACTOR
import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR
import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
@@ -39,7 +38,6 @@
import com.android.systemui.communal.domain.interactor.setCommunalAvailable
import com.android.systemui.communal.domain.interactor.setCommunalV2ConfigEnabled
import com.android.systemui.communal.shared.model.CommunalScenes
-import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepositorySpy
import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
@@ -70,7 +68,6 @@
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.advanceTimeBy
-import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -103,10 +100,7 @@
@JvmStatic
@Parameters(name = "{0}")
fun getParams(): List<FlagsParameterization> {
- return FlagsParameterization.allCombinationsOf(
- FLAG_COMMUNAL_SCENE_KTF_REFACTOR,
- FLAG_GLANCEABLE_HUB_V2,
- )
+ return FlagsParameterization.allCombinationsOf(FLAG_GLANCEABLE_HUB_V2)
}
}
@@ -174,22 +168,6 @@
@Test
@EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
- @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR, FLAG_GLANCEABLE_HUB_V2)
- fun testTransitionToLockscreen_onWake_canDream_glanceableHubAvailable() =
- kosmos.runTest {
- whenever(dreamManager.canStartDreaming(anyBoolean())).thenReturn(true)
- setCommunalAvailable(true)
-
- powerInteractor.setAwakeForTest()
-
- // If dreaming is possible and communal is available, then we should transition to
- // GLANCEABLE_HUB when waking up due to power button press.
- assertThat(transitionRepository)
- .startedTransition(from = KeyguardState.DOZING, to = KeyguardState.GLANCEABLE_HUB)
- }
-
- @Test
- @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR, FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
fun testTransitionToLockscreen_onWake_canDream_ktfRefactor() =
kosmos.runTest {
setCommunalAvailable(true)
@@ -243,24 +221,7 @@
}
@Test
- @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
- @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
- fun testTransitionToGlanceableHub_onWakeup_ifIdleOnCommunal_noOccludingActivity() =
- kosmos.runTest {
- fakeCommunalSceneRepository.setTransitionState(
- flowOf(ObservableTransitionState.Idle(CommunalScenes.Communal))
- )
-
- powerInteractor.setAwakeForTest()
-
- // Under default conditions, we should transition to LOCKSCREEN when waking up.
- assertThat(transitionRepository)
- .startedTransition(from = KeyguardState.DOZING, to = KeyguardState.GLANCEABLE_HUB)
- }
-
- @Test
@DisableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR, FLAG_SCENE_CONTAINER)
- @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
fun testTransitionToGlanceableHub_onWakeup_ifAvailable() =
kosmos.runTest {
setCommunalAvailable(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
index 5882cff..898a5c1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
@@ -23,7 +23,6 @@
import android.service.dream.dreamManager
import androidx.test.filters.SmallTest
import com.android.systemui.Flags
-import com.android.systemui.Flags.FLAG_COMMUNAL_SCENE_KTF_REFACTOR
import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
import com.android.systemui.Flags.glanceableHubV2
import com.android.systemui.SysuiTestCase
@@ -74,10 +73,7 @@
@JvmStatic
@Parameters(name = "{0}")
fun getParams(): List<FlagsParameterization> {
- return FlagsParameterization.allCombinationsOf(
- FLAG_COMMUNAL_SCENE_KTF_REFACTOR,
- FLAG_GLANCEABLE_HUB_V2,
- )
+ return FlagsParameterization.allCombinationsOf(FLAG_GLANCEABLE_HUB_V2)
.andSceneContainer()
}
}
@@ -202,7 +198,6 @@
}
@Test
- @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
@DisableFlags(Flags.FLAG_SCENE_CONTAINER)
fun testTransitionToGlanceableHubOnWake() =
kosmos.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt
index 63ed040..9a8e095 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt
@@ -32,16 +32,11 @@
package com.android.systemui.keyguard.domain.interactor
-import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.Flags
-import com.android.systemui.Flags.FLAG_COMMUNAL_SCENE_KTF_REFACTOR
import com.android.systemui.SysuiTestCase
-import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
-import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepositorySpy
import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
@@ -51,7 +46,6 @@
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.testKosmos
-import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -101,21 +95,4 @@
assertThat(transitionRepository)
.startedTransition(from = KeyguardState.OCCLUDED, to = KeyguardState.LOCKSCREEN)
}
-
- @Test
- @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
- @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
- fun testShowWhenLockedActivity_noLongerOnTop_transitionsToGlanceableHub_ifIdleOnCommunal() =
- testScope.runTest {
- kosmos.fakeCommunalSceneRepository.setTransitionState(
- flowOf(ObservableTransitionState.Idle(CommunalScenes.Communal))
- )
- runCurrent()
-
- kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(onTop = false)
- runCurrent()
-
- assertThat(transitionRepository)
- .startedTransition(from = KeyguardState.OCCLUDED, to = KeyguardState.GLANCEABLE_HUB)
- }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index d057f7a0..a090fab 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard.domain.interactor
import android.app.StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP
-import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
@@ -26,7 +25,6 @@
import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
import com.android.systemui.Flags
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
-import com.android.systemui.Flags.FLAG_COMMUNAL_SCENE_KTF_REFACTOR
import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
import com.android.systemui.Flags.glanceableHubV2
import com.android.systemui.SysuiTestCase
@@ -135,8 +133,7 @@
@JvmStatic
@Parameters(name = "{0}")
fun getParams(): List<FlagsParameterization> {
- return FlagsParameterization.allCombinationsOf(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
- .andSceneContainer()
+ return FlagsParameterization.allCombinationsOf().andSceneContainer()
}
}
@@ -468,38 +465,6 @@
@Test
@DisableSceneContainer
- @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
- fun dozingToGlanceableHub() =
- testScope.runTest {
- // GIVEN a prior transition has run to DOZING
- runTransitionAndSetWakefulness(KeyguardState.GLANCEABLE_HUB, KeyguardState.DOZING)
- runCurrent()
-
- // GIVEN the device is idle on the glanceable hub
- val idleTransitionState =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Idle(CommunalScenes.Communal)
- )
- communalInteractor.setTransitionState(idleTransitionState)
- runCurrent()
-
- // WHEN the device begins to wake
- keyguardRepository.setKeyguardShowing(true)
- powerInteractor.setAwakeForTest()
- advanceTimeBy(60L)
-
- assertThat(transitionRepository)
- .startedTransition(
- from = KeyguardState.DOZING,
- to = KeyguardState.GLANCEABLE_HUB,
- animatorAssertion = { it.isNotNull() },
- )
-
- coroutineContext.cancelChildren()
- }
-
- @Test
- @DisableSceneContainer
fun goneToDozing() =
testScope.runTest {
// GIVEN a device with AOD not available
@@ -625,40 +590,6 @@
@Test
@BrokenWithSceneContainer(339465026)
- @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
- fun goneToGlanceableHub() =
- testScope.runTest {
- // GIVEN a prior transition has run to GONE
- runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.GONE)
-
- // GIVEN the device is idle on the glanceable hub
- val idleTransitionState =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Idle(CommunalScenes.Communal)
- )
- communalInteractor.setTransitionState(idleTransitionState)
- runCurrent()
-
- // WHEN the keyguard starts to show
- keyguardRepository.setKeyguardShowing(true)
- runCurrent()
-
- assertThat(transitionRepository)
- .startedTransition(
- to = KeyguardState.GLANCEABLE_HUB,
- from = KeyguardState.GONE,
- ownerName =
- FromGoneTransitionInteractor::class.simpleName +
- "(keyguard interactor says keyguard is showing)",
- animatorAssertion = { it.isNotNull() },
- )
-
- coroutineContext.cancelChildren()
- }
-
- @Test
- @BrokenWithSceneContainer(339465026)
- @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
fun goneToGlanceableHub_communalKtfRefactor() =
testScope.runTest {
// GIVEN a prior transition has run to GONE
@@ -1070,84 +1001,6 @@
@Test
@BrokenWithSceneContainer(339465026)
- @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
- fun occludedToGlanceableHub() =
- testScope.runTest {
- // GIVEN a device on lockscreen
- keyguardRepository.setKeyguardShowing(true)
- runCurrent()
-
- // GIVEN the device is idle on the glanceable hub
- val idleTransitionState =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Idle(CommunalScenes.Communal)
- )
- communalInteractor.setTransitionState(idleTransitionState)
- runCurrent()
-
- // GIVEN a prior transition has run to OCCLUDED
- runTransitionAndSetWakefulness(KeyguardState.GLANCEABLE_HUB, KeyguardState.OCCLUDED)
- keyguardRepository.setKeyguardOccluded(true)
- runCurrent()
-
- // WHEN occlusion ends
- keyguardRepository.setKeyguardOccluded(false)
- runCurrent()
-
- // THEN a transition to GLANCEABLE_HUB should occur
- assertThat(transitionRepository)
- .startedTransition(
- ownerName = FromOccludedTransitionInteractor::class.simpleName,
- from = KeyguardState.OCCLUDED,
- to = KeyguardState.GLANCEABLE_HUB,
- animatorAssertion = { it.isNotNull() },
- )
-
- coroutineContext.cancelChildren()
- }
-
- @Test
- @BrokenWithSceneContainer(339465026)
- @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
- fun occludedToGlanceableHubWhenInitiallyOnHub() =
- testScope.runTest {
- // GIVEN a device on lockscreen and communal is available
- keyguardRepository.setKeyguardShowing(true)
- kosmos.setCommunalAvailable(true)
- runCurrent()
-
- // GIVEN a prior transition has run to OCCLUDED from GLANCEABLE_HUB
- runTransitionAndSetWakefulness(KeyguardState.GLANCEABLE_HUB, KeyguardState.OCCLUDED)
- keyguardRepository.setKeyguardOccluded(true)
- runCurrent()
-
- // GIVEN on blank scene
- val idleTransitionState =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Idle(CommunalScenes.Blank)
- )
- communalInteractor.setTransitionState(idleTransitionState)
- runCurrent()
-
- // WHEN occlusion ends
- keyguardRepository.setKeyguardOccluded(false)
- runCurrent()
-
- // THEN a transition to GLANCEABLE_HUB should occur
- assertThat(transitionRepository)
- .startedTransition(
- ownerName = FromOccludedTransitionInteractor::class.simpleName,
- from = KeyguardState.OCCLUDED,
- to = KeyguardState.GLANCEABLE_HUB,
- animatorAssertion = { it.isNotNull() },
- )
-
- coroutineContext.cancelChildren()
- }
-
- @Test
- @BrokenWithSceneContainer(339465026)
- @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
fun occludedToGlanceableHub_communalKtfRefactor() =
testScope.runTest {
// GIVEN a device on lockscreen and communal is available
@@ -1390,48 +1243,6 @@
@Test
@DisableSceneContainer
- @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
- fun dreamingToGlanceableHub() =
- testScope.runTest {
- // GIVEN a prior transition has run to DREAMING
- keyguardRepository.setDreaming(true)
- runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.DREAMING)
- runCurrent()
-
- // WHEN a transition to the glanceable hub starts
- val currentScene = CommunalScenes.Blank
- val targetScene = CommunalScenes.Communal
-
- val progress = MutableStateFlow(0f)
- val transitionState =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Transition(
- fromScene = currentScene,
- toScene = targetScene,
- currentScene = flowOf(targetScene),
- progress = progress,
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
- )
- )
- communalInteractor.setTransitionState(transitionState)
- progress.value = .1f
- runCurrent()
-
- assertThat(transitionRepository)
- .startedTransition(
- ownerName = FromDreamingTransitionInteractor::class.simpleName,
- from = KeyguardState.DREAMING,
- to = KeyguardState.GLANCEABLE_HUB,
- animatorAssertion = { it.isNull() }, // transition should be manually animated
- )
-
- coroutineContext.cancelChildren()
- }
-
- @Test
- @DisableSceneContainer
- @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
fun dreamingToGlanceableHub_communalKtfRefactor() =
testScope.runTest {
// GIVEN a prior transition has run to DREAMING
@@ -1596,66 +1407,6 @@
@Test
@DisableSceneContainer
- @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
- fun lockscreenToGlanceableHub() =
- testScope.runTest {
- // GIVEN a prior transition has run to LOCKSCREEN
- runTransitionAndSetWakefulness(KeyguardState.AOD, KeyguardState.LOCKSCREEN)
- runCurrent()
-
- // WHEN a glanceable hub transition starts
- val currentScene = CommunalScenes.Blank
- val targetScene = CommunalScenes.Communal
-
- val progress = MutableStateFlow(0f)
- val transitionState =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Transition(
- fromScene = currentScene,
- toScene = targetScene,
- currentScene = flowOf(targetScene),
- progress = progress,
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
- )
- )
- communalInteractor.setTransitionState(transitionState)
- progress.value = .1f
- runCurrent()
-
- // THEN a transition from LOCKSCREEN => GLANCEABLE_HUB should occur
- assertThat(transitionRepository)
- .startedTransition(
- ownerName = FromLockscreenTransitionInteractor::class.simpleName,
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.GLANCEABLE_HUB,
- animatorAssertion = { it.isNull() }, // transition should be manually animated
- )
-
- // WHEN the user stops dragging and the glanceable hub opening is cancelled
- clearInvocations(transitionRepository)
- runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.GLANCEABLE_HUB)
- val idleTransitionState =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Idle(currentScene)
- )
- communalInteractor.setTransitionState(idleTransitionState)
- runCurrent()
-
- // THEN a transition from LOCKSCREEN => GLANCEABLE_HUB should occur
- assertThat(transitionRepository)
- .startedTransition(
- ownerName = FromLockscreenTransitionInteractor::class.simpleName,
- from = KeyguardState.GLANCEABLE_HUB,
- to = KeyguardState.LOCKSCREEN,
- )
-
- coroutineContext.cancelChildren()
- }
-
- @Test
- @DisableSceneContainer
- @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
fun lockscreenToGlanceableHub_communalKtfRefactor() =
testScope.runTest {
// GIVEN a prior transition has run to LOCKSCREEN
@@ -1696,86 +1447,6 @@
@Test
@DisableSceneContainer
- @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
- fun glanceableHubToLockscreen() =
- testScope.runTest {
- // GIVEN a prior transition has run to GLANCEABLE_HUB
- runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.GLANCEABLE_HUB)
- runCurrent()
-
- // WHEN a transition away from glanceable hub starts
- val currentScene = CommunalScenes.Communal
- val targetScene = CommunalScenes.Blank
-
- val progress = MutableStateFlow(0f)
- val transitionState =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Transition(
- fromScene = currentScene,
- toScene = targetScene,
- currentScene = flowOf(targetScene),
- progress = progress,
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
- )
- )
- communalInteractor.setTransitionState(transitionState)
- progress.value = .1f
- runCurrent()
-
- assertThat(transitionRepository)
- .startedTransition(
- ownerName = FromGlanceableHubTransitionInteractor::class.simpleName,
- from = KeyguardState.GLANCEABLE_HUB,
- to = KeyguardState.LOCKSCREEN,
- animatorAssertion = { it.isNull() }, // transition should be manually animated
- )
-
- // WHEN the user stops dragging and the glanceable hub closing is cancelled
- clearInvocations(transitionRepository)
- runTransitionAndSetWakefulness(KeyguardState.GLANCEABLE_HUB, KeyguardState.LOCKSCREEN)
- val idleTransitionState =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Idle(currentScene)
- )
- communalInteractor.setTransitionState(idleTransitionState)
- runCurrent()
-
- assertThat(transitionRepository)
- .startedTransition(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.GLANCEABLE_HUB,
- )
-
- coroutineContext.cancelChildren()
- }
-
- @Test
- @DisableSceneContainer
- @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
- fun glanceableHubToDozing() =
- testScope.runTest {
- // GIVEN a prior transition has run to GLANCEABLE_HUB
- runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.GLANCEABLE_HUB)
-
- // WHEN the device begins to sleep
- powerInteractor.setAsleepForTest()
- runCurrent()
-
- assertThat(transitionRepository)
- .startedTransition(
- ownerName = FromGlanceableHubTransitionInteractor::class.simpleName,
- from = KeyguardState.GLANCEABLE_HUB,
- to = KeyguardState.DOZING,
- animatorAssertion = { it.isNotNull() },
- )
-
- coroutineContext.cancelChildren()
- }
-
- @Test
- @DisableSceneContainer
- @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
fun glanceableHubToDozing_communalKtfRefactor() =
testScope.runTest {
// GIVEN a prior transition has run to GLANCEABLE_HUB
@@ -1844,39 +1515,6 @@
@Test
@BrokenWithSceneContainer(339465026)
- @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
- fun glanceableHubToOccluded() =
- testScope.runTest {
- // GIVEN a prior transition has run to GLANCEABLE_HUB
- runTransitionAndSetWakefulness(KeyguardState.GONE, KeyguardState.GLANCEABLE_HUB)
- runCurrent()
-
- // GIVEN the device is idle on the glanceable hub
- val idleTransitionState =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Idle(CommunalScenes.Communal)
- )
- communalInteractor.setTransitionState(idleTransitionState)
- runCurrent()
-
- // WHEN the keyguard is occluded
- keyguardRepository.setKeyguardOccluded(true)
- runCurrent()
-
- assertThat(transitionRepository)
- .startedTransition(
- ownerName = FromGlanceableHubTransitionInteractor::class.simpleName,
- from = KeyguardState.GLANCEABLE_HUB,
- to = KeyguardState.OCCLUDED,
- animatorAssertion = { it.isNotNull() },
- )
-
- coroutineContext.cancelChildren()
- }
-
- @Test
- @BrokenWithSceneContainer(339465026)
- @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
fun glanceableHubToOccluded_communalKtfRefactor() =
testScope.runTest {
// GIVEN device is not dreaming
@@ -1905,30 +1543,6 @@
@Test
@DisableSceneContainer
- @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
- fun glanceableHubToGone() =
- testScope.runTest {
- // GIVEN a prior transition has run to GLANCEABLE_HUB
- runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.GLANCEABLE_HUB)
-
- // WHEN keyguard goes away
- keyguardRepository.setKeyguardGoingAway(true)
- runCurrent()
-
- assertThat(transitionRepository)
- .startedTransition(
- ownerName = FromGlanceableHubTransitionInteractor::class.simpleName,
- from = KeyguardState.GLANCEABLE_HUB,
- to = KeyguardState.GONE,
- animatorAssertion = { it.isNotNull() },
- )
-
- coroutineContext.cancelChildren()
- }
-
- @Test
- @DisableSceneContainer
- @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
fun glanceableHubToGone_communalKtfRefactor() =
testScope.runTest {
// GIVEN a prior transition has run to GLANCEABLE_HUB
@@ -1953,7 +1567,7 @@
@Test
@DisableSceneContainer
- @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR, FLAG_GLANCEABLE_HUB_V2)
+ @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
fun glanceableHubToDreaming_v2() =
testScope.runTest {
kosmos.setCommunalV2Enabled(true)
@@ -1985,55 +1599,7 @@
}
@Test
- @DisableSceneContainer
- @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
- fun glanceableHubToDreaming() =
- testScope.runTest {
- runCurrent()
-
- // GIVEN that we are dreaming and not dozing
- keyguardRepository.setDreaming(true)
- keyguardRepository.setDozeTransitionModel(
- DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
- )
- advanceTimeBy(600L)
-
- // GIVEN a prior transition has run to GLANCEABLE_HUB
- runTransitionAndSetWakefulness(KeyguardState.DREAMING, KeyguardState.GLANCEABLE_HUB)
- runCurrent()
-
- // WHEN a transition away from glanceable hub starts
- val currentScene = CommunalScenes.Communal
- val targetScene = CommunalScenes.Blank
-
- val transitionState =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Transition(
- fromScene = currentScene,
- toScene = targetScene,
- currentScene = flowOf(targetScene),
- progress = flowOf(0f, 0.1f),
- isInitiatedByUserInput = false,
- isUserInputOngoing = flowOf(false),
- )
- )
- communalSceneInteractor.setTransitionState(transitionState)
- runCurrent()
-
- assertThat(transitionRepository)
- .startedTransition(
- ownerName = FromGlanceableHubTransitionInteractor::class.simpleName,
- from = KeyguardState.GLANCEABLE_HUB,
- to = KeyguardState.DREAMING,
- animatorAssertion = { it.isNull() }, // transition should be manually animated
- )
-
- coroutineContext.cancelChildren()
- }
-
- @Test
@BrokenWithSceneContainer(339465026)
- @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
fun glanceableHubToOccludedDoesNotTriggerWhenDreamStateChanges_communalKtfRefactor() =
testScope.runTest {
// GIVEN that we are dreaming and not dozing
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 847044a..95d9c8f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -386,6 +386,7 @@
verify(mMediaSwitchingController).logInteractionAdjustVolume(mMediaDevice1);
}
+ @DisableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_DEVICE_GROUPING)
@Test
public void onBindViewHolder_bindSelectableDevice_verifyView() {
List<MediaDevice> selectableDevices = new ArrayList<>();
@@ -396,10 +397,55 @@
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mEndTouchArea.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mCheckBox.isChecked()).isFalse();
assertThat(mViewHolder.mEndClickIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_2);
assertThat(mViewHolder.mContainerLayout.isFocusable()).isTrue();
+
+ mViewHolder.mCheckBox.performClick();
+ verify(mMediaSwitchingController).addDeviceToPlayMedia(mMediaDevice2);
+ }
+
+ @DisableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_DEVICE_GROUPING)
+ @Test
+ public void onBindViewHolder_bindDeselectableDevice_verifyView() {
+ when(mMediaSwitchingController.getSelectedMediaDevice()).thenReturn(
+ List.of(mMediaDevice1, mMediaDevice2));
+ when(mMediaSwitchingController.getDeselectableMediaDevice()).thenReturn(
+ List.of(mMediaDevice1, mMediaDevice2));
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
+
+ assertThat(mViewHolder.mEndTouchArea.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mCheckBox.isChecked()).isTrue();
+ assertThat(mViewHolder.mEndClickIcon.getVisibility()).isEqualTo(View.GONE);
+
+ mViewHolder.mCheckBox.performClick();
+ verify(mMediaSwitchingController).removeDeviceFromPlayMedia(mMediaDevice2);
+ }
+
+ @DisableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_DEVICE_GROUPING)
+ @Test
+ public void onBindViewHolder_changingSelectedValue_doesntTriggerChangeListener() {
+ List<MediaDevice> selectableDevices = List.of(mMediaDevice2);
+ List<MediaDevice> selectedDevices = new ArrayList<>();
+ selectedDevices.add(mMediaDevice1);
+ when(mMediaSwitchingController.getSelectableMediaDevice()).thenReturn(selectableDevices);
+ when(mMediaSwitchingController.getSelectedMediaDevice()).thenReturn(selectedDevices);
+
+ // mMediaDevice2 is selected
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
+ assertThat(mViewHolder.mCheckBox.isChecked()).isFalse();
+
+ // changing the selected state programmatically (not a user click)
+ selectedDevices.add(mMediaDevice2);
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
+ assertThat(mViewHolder.mCheckBox.isChecked()).isTrue();
+
+ // The onCheckedChangeListener is not invoked
+ verify(mMediaSwitchingController, never()).addDeviceToPlayMedia(mMediaDevice2);
+ verify(mMediaSwitchingController, never()).removeDeviceFromPlayMedia(mMediaDevice2);
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
index 896ca26..3c8857f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -17,11 +17,14 @@
package com.android.systemui.qs
import android.content.res.Configuration
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.view.ContextThemeWrapper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.haptics.qs.QSLongPressEffect
@@ -33,6 +36,7 @@
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
import com.android.systemui.util.leak.RotationUtils
import javax.inject.Provider
@@ -63,6 +67,7 @@
@Mock private lateinit var tile: QSTile
@Mock private lateinit var tileLayout: TileLayout
@Captor private lateinit var captor: ArgumentCaptor<QSPanel.OnConfigurationChangedListener>
+ @Captor private lateinit var configCaptor: ArgumentCaptor<ConfigurationListener>
@Mock private lateinit var longPressEffectProvider: Provider<QSLongPressEffect>
@Mock private lateinit var mediaCarouselInteractor: MediaCarouselInteractor
@Mock private lateinit var configurationController: ConfigurationController
@@ -135,7 +140,8 @@
}
@Test
- fun mediaExpansion_afterConfigChange_inLandscape_collapsedInLandscapeTrue_updatesToCollapsed() {
+ @DisableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
+ fun mediaExpansion_afterConfigChange_inLandscape_collapsedInLandscapeTrue_updatesToCollapsed_old() {
verify(quickQSPanel).addOnConfigurationChangedListener(captor.capture())
// verify that media starts in the expanded state by default
@@ -150,7 +156,24 @@
}
@Test
- fun mediaExpansion_afterConfigChange_landscape_collapsedInLandscapeFalse_remainsExpanded() {
+ @EnableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
+ fun mediaExpansion_afterConfigChange_inLandscape_collapsedInLandscapeTrue_updatesToCollapsed() {
+ verify(configurationController).addCallback(configCaptor.capture())
+
+ // verify that media starts in the expanded state by default
+ verify(mediaHost).expansion = MediaHostState.EXPANDED
+
+ // Rotate device, verify media size updated to collapsed
+ usingCollapsedLandscapeMedia = true
+ controller.setRotation(RotationUtils.ROTATION_LANDSCAPE)
+ configCaptor.allValues.forEach { it.onConfigChanged(Configuration.EMPTY) }
+
+ verify(mediaHost).expansion = MediaHostState.COLLAPSED
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
+ fun mediaExpansion_afterConfigChange_landscape_collapsedInLandscapeFalse_remainsExpanded_old() {
verify(quickQSPanel).addOnConfigurationChangedListener(captor.capture())
reset(mediaHost)
@@ -161,6 +184,19 @@
verify(mediaHost).expansion = MediaHostState.EXPANDED
}
+ @Test
+ @EnableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
+ fun mediaExpansion_afterConfigChange_landscape_collapsedInLandscapeFalse_remainsExpanded() {
+ verify(configurationController).addCallback(configCaptor.capture())
+ reset(mediaHost)
+
+ usingCollapsedLandscapeMedia = false
+ controller.setRotation(RotationUtils.ROTATION_LANDSCAPE)
+ configCaptor.allValues.forEach { it.onConfigChanged(Configuration.EMPTY) }
+
+ verify(mediaHost).expansion = MediaHostState.EXPANDED
+ }
+
class TestQuickQSPanelController(
view: QuickQSPanel,
qsHost: QSHost,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
index adaebbd..5527393 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
@@ -25,8 +25,10 @@
import com.android.compose.animation.scene.DefaultEdgeDetector
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.fakeFalsingManager
-import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.power.data.repository.fakePowerRepository
@@ -44,17 +46,15 @@
import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.statusbar.data.repository.fakeRemoteInputRepository
import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -64,10 +64,6 @@
private val kosmos = testKosmos()
private val testScope by lazy { kosmos.testScope }
- private val sceneInteractor by lazy { kosmos.sceneInteractor }
- private val fakeSceneDataSource by lazy { kosmos.fakeSceneDataSource }
- private val sceneContainerConfig by lazy { kosmos.sceneContainerConfig }
- private val fakeRemoteInputRepository by lazy { kosmos.fakeRemoteInputRepository }
private val falsingManager by lazy { kosmos.fakeFalsingManager }
private val view = mock<View>()
@@ -91,14 +87,14 @@
@Test
fun activate_setsMotionEventHandler() =
- testScope.runTest {
+ kosmos.runTest {
runCurrent()
assertThat(motionEventHandler).isNotNull()
}
@Test
fun deactivate_clearsMotionEventHandler() =
- testScope.runTest {
+ kosmos.runTest {
activationJob.cancel()
runCurrent()
@@ -107,7 +103,7 @@
@Test
fun isVisible() =
- testScope.runTest {
+ kosmos.runTest {
assertThat(underTest.isVisible).isTrue()
sceneInteractor.setVisible(false, "reason")
@@ -121,7 +117,7 @@
@Test
fun sceneTransition() =
- testScope.runTest {
+ kosmos.runTest {
val currentScene by collectLastValue(underTest.currentScene)
assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
@@ -132,7 +128,7 @@
@Test
fun canChangeScene_whenAllowed_switchingFromGone_returnsTrue() =
- testScope.runTest {
+ kosmos.runTest {
val currentScene by collectLastValue(underTest.currentScene)
fakeSceneDataSource.changeScene(toScene = Scenes.Gone)
runCurrent()
@@ -149,7 +145,7 @@
@Test
fun canChangeScene_whenAllowed_switchingFromLockscreen_returnsTrue() =
- testScope.runTest {
+ kosmos.runTest {
val currentScene by collectLastValue(underTest.currentScene)
fakeSceneDataSource.changeScene(toScene = Scenes.Lockscreen)
runCurrent()
@@ -166,7 +162,7 @@
@Test
fun canChangeScene_whenNotAllowed_fromLockscreen_toFalsingProtectedScenes_returnsFalse() =
- testScope.runTest {
+ kosmos.runTest {
falsingManager.setIsFalseTouch(true)
val currentScene by collectLastValue(underTest.currentScene)
fakeSceneDataSource.changeScene(toScene = Scenes.Lockscreen)
@@ -188,7 +184,7 @@
@Test
fun canChangeScene_whenNotAllowed_fromLockscreen_toFalsingUnprotectedScenes_returnsTrue() =
- testScope.runTest {
+ kosmos.runTest {
falsingManager.setIsFalseTouch(true)
val currentScene by collectLastValue(underTest.currentScene)
fakeSceneDataSource.changeScene(toScene = Scenes.Lockscreen)
@@ -209,7 +205,7 @@
@Test
fun canChangeScene_whenNotAllowed_fromGone_toAnyOtherScene_returnsTrue() =
- testScope.runTest {
+ kosmos.runTest {
falsingManager.setIsFalseTouch(true)
val currentScene by collectLastValue(underTest.currentScene)
fakeSceneDataSource.changeScene(toScene = Scenes.Gone)
@@ -226,8 +222,70 @@
}
@Test
+ fun canShowOrReplaceOverlay_whenAllowed_showingWhileOnGone_returnsTrue() =
+ kosmos.runTest {
+ val currentScene by collectLastValue(underTest.currentScene)
+ fakeSceneDataSource.changeScene(toScene = Scenes.Gone)
+ runCurrent()
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+
+ sceneContainerConfig.overlayKeys.forEach { overlay ->
+ assertWithMessage("Overlay $overlay incorrectly protected when allowed")
+ .that(underTest.canShowOrReplaceOverlay(newlyShown = overlay))
+ .isTrue()
+ }
+ }
+
+ @Test
+ fun canShowOrReplaceOverlay_whenAllowed_showingWhileOnLockscreen_returnsTrue() =
+ kosmos.runTest {
+ val currentScene by collectLastValue(underTest.currentScene)
+ fakeSceneDataSource.changeScene(toScene = Scenes.Lockscreen)
+ runCurrent()
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+
+ sceneContainerConfig.overlayKeys.forEach { overlay ->
+ assertWithMessage("Overlay $overlay incorrectly protected when allowed")
+ .that(underTest.canShowOrReplaceOverlay(newlyShown = overlay))
+ .isTrue()
+ }
+ }
+
+ @Test
+ fun canShowOrReplaceOverlay_whenNotAllowed_whileOnLockscreen_returnsFalse() =
+ kosmos.runTest {
+ falsingManager.setIsFalseTouch(true)
+ val currentScene by collectLastValue(underTest.currentScene)
+ fakeSceneDataSource.changeScene(toScene = Scenes.Lockscreen)
+ runCurrent()
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+
+ sceneContainerConfig.overlayKeys.forEach { overlay ->
+ assertWithMessage("Protected overlay $overlay not properly protected")
+ .that(underTest.canShowOrReplaceOverlay(newlyShown = overlay))
+ .isFalse()
+ }
+ }
+
+ @Test
+ fun canShowOrReplaceOverlay_whenNotAllowed_whileOnGone_returnsTrue() =
+ kosmos.runTest {
+ falsingManager.setIsFalseTouch(true)
+ val currentScene by collectLastValue(underTest.currentScene)
+ fakeSceneDataSource.changeScene(toScene = Scenes.Gone)
+ runCurrent()
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+
+ sceneContainerConfig.overlayKeys.forEach { overlay ->
+ assertWithMessage("Protected overlay $overlay not properly protected")
+ .that(underTest.canShowOrReplaceOverlay(newlyShown = overlay))
+ .isTrue()
+ }
+ }
+
+ @Test
fun userInput() =
- testScope.runTest {
+ kosmos.runTest {
assertThat(kosmos.fakePowerRepository.userTouchRegistered).isFalse()
underTest.onMotionEvent(mock())
assertThat(kosmos.fakePowerRepository.userTouchRegistered).isTrue()
@@ -235,7 +293,7 @@
@Test
fun userInputOnEmptySpace_insideEvent() =
- testScope.runTest {
+ kosmos.runTest {
assertThat(fakeRemoteInputRepository.areRemoteInputsClosed).isFalse()
val insideMotionEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0f, 0f, 0)
underTest.onEmptySpaceMotionEvent(insideMotionEvent)
@@ -244,7 +302,7 @@
@Test
fun userInputOnEmptySpace_outsideEvent_remoteInputActive() =
- testScope.runTest {
+ kosmos.runTest {
fakeRemoteInputRepository.isRemoteInputActive.value = true
assertThat(fakeRemoteInputRepository.areRemoteInputsClosed).isFalse()
val outsideMotionEvent = MotionEvent.obtain(0, 0, ACTION_OUTSIDE, 0f, 0f, 0)
@@ -254,7 +312,7 @@
@Test
fun userInputOnEmptySpace_outsideEvent_remoteInputInactive() =
- testScope.runTest {
+ kosmos.runTest {
fakeRemoteInputRepository.isRemoteInputActive.value = false
assertThat(fakeRemoteInputRepository.areRemoteInputsClosed).isFalse()
val outsideMotionEvent = MotionEvent.obtain(0, 0, ACTION_OUTSIDE, 0f, 0f, 0)
@@ -264,7 +322,7 @@
@Test
fun remoteUserInteraction_keepsContainerVisible() =
- testScope.runTest {
+ kosmos.runTest {
sceneInteractor.setVisible(false, "reason")
runCurrent()
assertThat(underTest.isVisible).isFalse()
@@ -272,9 +330,7 @@
runCurrent()
assertThat(underTest.isVisible).isTrue()
- underTest.onMotionEvent(
- mock { whenever(actionMasked).thenReturn(MotionEvent.ACTION_UP) }
- )
+ underTest.onMotionEvent(mock { on { actionMasked } doReturn MotionEvent.ACTION_UP })
runCurrent()
assertThat(underTest.isVisible).isFalse()
@@ -282,7 +338,7 @@
@Test
fun getActionableContentKey_noOverlays_returnsCurrentScene() =
- testScope.runTest {
+ kosmos.runTest {
val currentScene by collectLastValue(underTest.currentScene)
val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
@@ -300,7 +356,7 @@
@Test
fun getActionableContentKey_multipleOverlays_returnsTopOverlay() =
- testScope.runTest {
+ kosmos.runTest {
val currentScene by collectLastValue(underTest.currentScene)
val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
fakeSceneDataSource.showOverlay(Overlays.QuickSettingsShade)
@@ -321,7 +377,7 @@
@Test
fun edgeDetector_singleShade_usesDefaultEdgeDetector() =
- testScope.runTest {
+ kosmos.runTest {
val shadeMode by collectLastValue(kosmos.shadeMode)
kosmos.enableSingleShade()
@@ -331,7 +387,7 @@
@Test
fun edgeDetector_splitShade_usesDefaultEdgeDetector() =
- testScope.runTest {
+ kosmos.runTest {
val shadeMode by collectLastValue(kosmos.shadeMode)
kosmos.enableSplitShade()
@@ -341,7 +397,7 @@
@Test
fun edgeDetector_dualShade_narrowScreen_usesSceneContainerSwipeDetector() =
- testScope.runTest {
+ kosmos.runTest {
val shadeMode by collectLastValue(kosmos.shadeMode)
kosmos.enableDualShade(wideLayout = false)
@@ -352,7 +408,7 @@
@Test
fun edgeDetector_dualShade_wideScreen_usesSceneContainerSwipeDetector() =
- testScope.runTest {
+ kosmos.runTest {
val shadeMode by collectLastValue(kosmos.shadeMode)
kosmos.enableDualShade(wideLayout = true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/ConversationNotificationProcessorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/ConversationNotificationProcessorTest.kt
new file mode 100644
index 0000000..67d0ade
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/ConversationNotificationProcessorTest.kt
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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
+
+import android.app.Flags
+import android.app.Notification
+import android.app.Notification.EXTRA_SUMMARIZED_CONTENT
+import android.app.Person
+import android.content.pm.LauncherApps
+import android.content.pm.launcherApps
+import android.graphics.drawable.Icon
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.testing.TestableLooper.RunWithLooper
+import android.text.SpannableStringBuilder
+import android.text.style.ImageSpan
+import android.text.style.StyleSpan
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.RankingBuilder
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinderLogger
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper
+import com.android.systemui.statusbar.notification.row.notificationRowContentBinderLogger
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@RunWithLooper
+class ConversationNotificationProcessorTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private lateinit var conversationNotificationProcessor: ConversationNotificationProcessor
+ private lateinit var testHelper: NotificationTestHelper
+ private lateinit var launcherApps: LauncherApps
+ private lateinit var logger: NotificationRowContentBinderLogger
+ private lateinit var conversationNotificationManager: ConversationNotificationManager
+
+ @Before
+ fun setup() {
+ launcherApps = kosmos.launcherApps
+ conversationNotificationManager = kosmos.conversationNotificationManager
+ logger = kosmos.notificationRowContentBinderLogger
+ testHelper = NotificationTestHelper(mContext, mDependency)
+
+ conversationNotificationProcessor =
+ ConversationNotificationProcessor(
+ context,
+ launcherApps,
+ conversationNotificationManager,
+ )
+ }
+
+ @Test
+ fun processNotification_notMessagingStyle() {
+ val nb = Notification.Builder(mContext).setSmallIcon(R.drawable.ic_person)
+ val newRow: ExpandableNotificationRow = testHelper.createRow(nb.build())
+
+ assertThat(conversationNotificationProcessor.processNotification(newRow.entry, nb, logger))
+ .isNull()
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_NM_SUMMARIZATION, Flags.FLAG_NM_SUMMARIZATION_UI)
+ fun processNotification_messagingStyleWithSummarization_flagOff() {
+ val summarization = "hello"
+ val nb = getMessagingNotification()
+ val newRow: ExpandableNotificationRow = testHelper.createRow(nb.build())
+ newRow.entry.setRanking(
+ RankingBuilder(newRow.entry.ranking).setSummarization(summarization).build()
+ )
+
+ assertThat(conversationNotificationProcessor.processNotification(newRow.entry, nb, logger))
+ .isNotNull()
+ assertThat(nb.build().extras.getCharSequence(EXTRA_SUMMARIZED_CONTENT)).isNull()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NM_SUMMARIZATION)
+ fun processNotification_messagingStyleWithSummarization() {
+ val summarization = "hello"
+ val nb = getMessagingNotification()
+ val newRow: ExpandableNotificationRow = testHelper.createRow(nb.build())
+ newRow.entry.setRanking(
+ RankingBuilder(newRow.entry.ranking).setSummarization(summarization).build()
+ )
+
+ assertThat(conversationNotificationProcessor.processNotification(newRow.entry, nb, logger))
+ .isNotNull()
+
+ val processedSummary = nb.build().extras.getCharSequence(EXTRA_SUMMARIZED_CONTENT)
+ assertThat(processedSummary.toString()).isEqualTo("x$summarization")
+
+ val checkSpans = SpannableStringBuilder(processedSummary)
+ assertThat(
+ checkSpans.getSpans(
+ /* queryStart = */ 0,
+ /* queryEnd = */ 1,
+ /* kind = */ ImageSpan::class.java,
+ )
+ )
+ .isNotNull()
+
+ assertThat(
+ processedSummary?.let {
+ checkSpans.getSpans(
+ /* queryStart = */ 0,
+ /* queryEnd = */ it.length,
+ /* kind = */ StyleSpan::class.java,
+ )
+ }
+ )
+ .isNotNull()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NM_SUMMARIZATION)
+ fun processNotification_messagingStyleWithoutSummarization() {
+ val nb = getMessagingNotification()
+ val newRow: ExpandableNotificationRow = testHelper.createRow(nb.build())
+ assertThat(conversationNotificationProcessor.processNotification(newRow.entry, nb, logger))
+ .isNotNull()
+ assertThat(nb.build().extras.getCharSequence(EXTRA_SUMMARIZED_CONTENT)).isNull()
+ }
+
+ private fun getMessagingNotification(): Notification.Builder {
+ val displayName = "Display Name"
+ val messageText = "Message Text"
+ val personIcon = Icon.createWithResource(mContext, R.drawable.ic_person)
+ val testPerson = Person.Builder().setName(displayName).setIcon(personIcon).build()
+ val messagingStyle = Notification.MessagingStyle(testPerson)
+ messagingStyle.addMessage(
+ Notification.MessagingStyle.Message(messageText, System.currentTimeMillis(), testPerson)
+ )
+ return Notification.Builder(mContext)
+ .setSmallIcon(R.drawable.ic_person)
+ .setStyle(messagingStyle)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index da31cd9..7d406b4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -209,6 +209,26 @@
}
@Test
+ public void testInflationProcessesMessagingStyle() throws Exception {
+ String displayName = "Display Name";
+ String messageText = "Message Text";
+ Icon personIcon = Icon.createWithResource(
+ mContext, com.android.systemui.res.R.drawable.ic_person);
+ Person testPerson = new Person.Builder().setName(displayName).setIcon(personIcon).build();
+ Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle(testPerson);
+ messagingStyle.addMessage(new Notification.MessagingStyle.Message(
+ messageText, System.currentTimeMillis(), testPerson));
+ Notification messageNotif = new Notification.Builder(mContext)
+ .setSmallIcon(com.android.systemui.res.R.drawable.ic_person)
+ .setStyle(messagingStyle)
+ .build();
+ ExpandableNotificationRow newRow = mHelper.createRow(messageNotif);
+ inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, newRow);
+
+ verify(mConversationNotificationProcessor).processNotification(any(), any(), any());
+ }
+
+ @Test
@Ignore
public void testInflationIsRetriedIfAsyncFails() throws Exception {
NotificationContentInflater.InflationProgress result =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
index c77b09a..82eca37 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
@@ -587,6 +587,35 @@
Assert.assertFalse(hasText(publicView, contentTitle))
}
+ @Test
+ @Throws(java.lang.Exception::class)
+ @EnableFlags(android.app.Flags.FLAG_NM_SUMMARIZATION)
+ fun testAllMessagingStyleProcessedAsConversations() {
+ val displayName = "Display Name"
+ val messageText = "Message Text"
+ val personIcon = Icon.createWithResource(mContext, R.drawable.ic_person)
+ val testPerson = Person.Builder().setName(displayName).setIcon(personIcon).build()
+ val messagingStyle = Notification.MessagingStyle(testPerson)
+ messagingStyle.addMessage(
+ Notification.MessagingStyle.Message(messageText, System.currentTimeMillis(), testPerson)
+ )
+ val messageNotif =
+ Notification.Builder(mContext)
+ .setSmallIcon(R.drawable.ic_person)
+ .setStyle(messagingStyle)
+ .build()
+ val newRow: ExpandableNotificationRow = testHelper.createRow(messageNotif)
+
+ inflateAndWait(
+ false,
+ notificationInflater,
+ FLAG_CONTENT_VIEW_ALL,
+ REDACTION_TYPE_NONE,
+ newRow,
+ )
+ verify(conversationNotificationProcessor).processNotification(any(), any(), any())
+ }
+
private class ExceptionHolder {
var exception: Exception? = null
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
index fc589b2..e36e855 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
@@ -19,11 +19,8 @@
import android.os.UserHandle
import android.provider.Settings
import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.compose.animation.scene.SceneKey
-import com.android.compose.animation.scene.TransitionKey
import com.android.internal.logging.UiEventLogger
import com.android.systemui.CoreStartable
-import com.android.systemui.Flags.communalSceneKtfRefactor
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
@@ -31,36 +28,24 @@
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalScenes.isCommunal
import com.android.systemui.communal.shared.model.CommunalTransitionKeys
-import com.android.systemui.communal.shared.model.EditModeState
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.dock.DockManager
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.statusbar.NotificationShadeWindowController
-import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.util.kotlin.emitOnStart
-import com.android.systemui.util.kotlin.getValue
import com.android.systemui.util.kotlin.sample
import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
import com.android.systemui.util.settings.SystemSettings
-import java.util.Optional
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
-import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.withContext
@@ -72,16 +57,12 @@
class CommunalSceneStartable
@Inject
constructor(
- private val dockManager: DockManager,
private val communalInteractor: CommunalInteractor,
private val communalSettingsInteractor: CommunalSettingsInteractor,
private val communalSceneInteractor: CommunalSceneInteractor,
- private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val keyguardInteractor: KeyguardInteractor,
private val systemSettings: SystemSettings,
- centralSurfacesOpt: Optional<CentralSurfaces>,
private val notificationShadeWindowController: NotificationShadeWindowController,
- @Application private val applicationScope: CoroutineScope,
@Background private val bgScope: CoroutineScope,
@Main private val mainDispatcher: CoroutineDispatcher,
private val uiEventLogger: UiEventLogger,
@@ -92,110 +73,11 @@
private var isDreaming: Boolean = false
- private val centralSurfaces: CentralSurfaces? by centralSurfacesOpt
-
override fun start() {
if (!communalSettingsInteractor.isCommunalFlagEnabled()) {
return
}
- if (!communalSceneKtfRefactor()) {
- // Handle automatically switching based on keyguard state.
- keyguardTransitionInteractor.startedKeyguardTransitionStep
- .mapLatest(::determineSceneAfterTransition)
- .filterNotNull()
- .onEach { (nextScene, nextTransition) ->
- // When launching a widget, we don't want to animate the scene change or the
- // Communal Hub will reveal the wallpaper even though it shouldn't. Instead we
- // snap to the new scene as part of the launch animation, once the activity
- // launch is done, so we don't change scene here.
- val delaySceneTransition =
- communalSceneInteractor.editModeState.value == EditModeState.STARTING ||
- communalSceneInteractor.isLaunchingWidget.value
- if (!delaySceneTransition) {
- communalSceneInteractor.changeScene(
- newScene = nextScene,
- loggingReason = "KTF syncing",
- transitionKey = nextTransition,
- )
- }
- }
- .launchIn(applicationScope)
- }
-
- // TODO(b/322787129): re-enable once custom animations are in place
- // Handle automatically switching to communal when docked.
- // dockManager
- // .retrieveIsDocked()
- // // Allow some time after docking to ensure the dream doesn't start. If the
- // dream
- // // starts, then we don't want to automatically transition to glanceable hub.
- // .debounce(DOCK_DEBOUNCE_DELAY)
- // .sample(keyguardTransitionInteractor.startedKeyguardState, ::Pair)
- // .onEach { (docked, lastStartedState) ->
- // if (docked && lastStartedState == KeyguardState.LOCKSCREEN) {
- // communalInteractor.onSceneChanged(CommunalScenes.Communal)
- // }
- // }
- // .launchIn(bgScope)
-
- systemSettings
- .observerFlow(Settings.System.SCREEN_OFF_TIMEOUT)
- // Read the setting value on start.
- .emitOnStart()
- .onEach {
- screenTimeout =
- systemSettings.getIntForUser(
- Settings.System.SCREEN_OFF_TIMEOUT,
- DEFAULT_SCREEN_TIMEOUT,
- UserHandle.USER_CURRENT,
- )
- }
- .launchIn(bgScope)
-
- // The hub mode timeout should start as soon as the user enters hub mode. At the end of the
- // timer, if the device is dreaming, hub mode should closed and reveal the dream. If the
- // dream is not running, nothing will happen. However if the dream starts again underneath
- // hub mode after the initial timeout expires, such as if the device is docked or the dream
- // app is updated by the Play store, a new timeout should be started.
- bgScope.launch {
- combine(
- communalSceneInteractor.currentScene,
- // Emit a value on start so the combine starts.
- communalInteractor.userActivity.emitOnStart(),
- ) { scene, _ ->
- // Only timeout if we're on the hub is open.
- scene.isCommunal()
- }
- .collectLatest { shouldTimeout ->
- cancelHubTimeout()
- if (shouldTimeout) {
- startHubTimeout()
- }
- }
- }
- bgScope.launch {
- keyguardInteractor.isDreaming
- .sample(communalSceneInteractor.currentScene, ::Pair)
- .collectLatest { (isDreaming, scene) ->
- this@CommunalSceneStartable.isDreaming = isDreaming
- if (scene.isCommunal() && isDreaming && timeoutJob == null) {
- // If dreaming starts after timeout has expired, ex. if dream restarts under
- // the hub, wait for IS_ABLE_TO_DREAM_DELAY_MS and then close the hub. The
- // delay is necessary so the KeyguardInteractor.isAbleToDream flow passes
- // through that same amount of delay and publishes a new value which is then
- // picked up by the HomeSceneFamilyResolver such that the next call to
- // SceneInteractor.changeScene(Home) will resolve "Home" to "Dream".
- delay(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS)
- communalSceneInteractor.changeScene(
- CommunalScenes.Blank,
- "dream started after timeout",
- )
- uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_TIMEOUT)
- }
- }
- }
-
bgScope.launch {
communalSceneInteractor.isIdleOnCommunal.collectLatest {
withContext(mainDispatcher) {
@@ -203,6 +85,75 @@
}
}
}
+
+ // In V2, the timeout is handled by PowerManagerService since we no longer keep the dream
+ // active underneath the hub.
+ if (!communalSettingsInteractor.isV2FlagEnabled()) {
+ systemSettings
+ .observerFlow(Settings.System.SCREEN_OFF_TIMEOUT)
+ // Read the setting value on start.
+ .emitOnStart()
+ .onEach {
+ screenTimeout =
+ systemSettings.getIntForUser(
+ Settings.System.SCREEN_OFF_TIMEOUT,
+ DEFAULT_SCREEN_TIMEOUT,
+ UserHandle.USER_CURRENT,
+ )
+ }
+ .launchIn(bgScope)
+
+ // The hub mode timeout should start as soon as the user enters hub mode. At the end of
+ // the
+ // timer, if the device is dreaming, hub mode should closed and reveal the dream. If the
+ // dream is not running, nothing will happen. However if the dream starts again
+ // underneath
+ // hub mode after the initial timeout expires, such as if the device is docked or the
+ // dream
+ // app is updated by the Play store, a new timeout should be started.
+ bgScope.launch {
+ combine(
+ communalSceneInteractor.currentScene,
+ // Emit a value on start so the combine starts.
+ communalInteractor.userActivity.emitOnStart(),
+ ) { scene, _ ->
+ // Only timeout if we're on the hub is open.
+ scene.isCommunal()
+ }
+ .collectLatest { shouldTimeout ->
+ cancelHubTimeout()
+ if (shouldTimeout) {
+ startHubTimeout()
+ }
+ }
+ }
+
+ bgScope.launch {
+ keyguardInteractor.isDreaming
+ .sample(communalSceneInteractor.currentScene, ::Pair)
+ .collectLatest { (isDreaming, scene) ->
+ this@CommunalSceneStartable.isDreaming = isDreaming
+ if (scene.isCommunal() && isDreaming && timeoutJob == null) {
+ // If dreaming starts after timeout has expired, ex. if dream restarts
+ // under
+ // the hub, wait for IS_ABLE_TO_DREAM_DELAY_MS and then close the hub.
+ // The
+ // delay is necessary so the KeyguardInteractor.isAbleToDream flow
+ // passes
+ // through that same amount of delay and publishes a new value which is
+ // then
+ // picked up by the HomeSceneFamilyResolver such that the next call to
+ // SceneInteractor.changeScene(Home) will resolve "Home" to "Dream".
+ delay(KeyguardInteractor.IS_ABLE_TO_DREAM_DELAY_MS)
+ communalSceneInteractor.changeScene(
+ CommunalScenes.Blank,
+ "dream started after timeout",
+ )
+ uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_TIMEOUT)
+ }
+ }
+ }
+ }
}
private fun cancelHubTimeout() {
@@ -231,49 +182,7 @@
}
}
- private suspend fun determineSceneAfterTransition(
- lastStartedTransition: TransitionStep
- ): Pair<SceneKey, TransitionKey>? {
- val to = lastStartedTransition.to
- val from = lastStartedTransition.from
- val docked = dockManager.isDocked
- val launchingActivityOverLockscreen =
- centralSurfaces?.isLaunchingActivityOverLockscreen ?: false
-
- return when {
- to == KeyguardState.OCCLUDED && !launchingActivityOverLockscreen -> {
- // Hide communal when an activity is started on keyguard, to ensure the activity
- // underneath the hub is shown. When launching activities over lockscreen, we only
- // change scenes once the activity launch animation is finished, so avoid
- // changing the scene here.
- Pair(CommunalScenes.Blank, CommunalTransitionKeys.SimpleFade)
- }
- to == KeyguardState.GLANCEABLE_HUB && from == KeyguardState.OCCLUDED -> {
- // When transitioning to the hub from an occluded state, fade out the hub without
- // doing any translation.
- Pair(CommunalScenes.Communal, CommunalTransitionKeys.SimpleFade)
- }
- // Transitioning to Blank scene when entering the edit mode will be handled separately
- // with custom animations.
- to == KeyguardState.GONE && !communalInteractor.editModeOpen.value ->
- Pair(CommunalScenes.Blank, CommunalTransitionKeys.SimpleFade)
- !docked && !KeyguardState.deviceIsAwakeInState(to) -> {
- // If the user taps the screen and wakes the device within this timeout, we don't
- // want to dismiss the hub
- delay(AWAKE_DEBOUNCE_DELAY)
- Pair(CommunalScenes.Blank, CommunalTransitionKeys.SimpleFade)
- }
- from == KeyguardState.DOZING && to == KeyguardState.GLANCEABLE_HUB -> {
- // Make sure the communal hub is showing when transitioning from dozing to hub.
- Pair(CommunalScenes.Communal, CommunalTransitionKeys.SimpleFade)
- }
- else -> null
- }
- }
-
companion object {
- val AWAKE_DEBOUNCE_DELAY = 5.seconds
- val DOCK_DEBOUNCE_DELAY = 1.seconds
val DEFAULT_SCREEN_TIMEOUT = 15000
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
index ec55401..740555f4 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
@@ -16,11 +16,11 @@
package com.android.systemui.communal.domain.interactor
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneTransitionLayout
import com.android.systemui.CoreStartable
-import com.android.systemui.Flags.communalSceneKtfRefactor
import com.android.systemui.communal.data.repository.CommunalSceneTransitionRepository
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.dagger.SysUISingleton
@@ -44,7 +44,6 @@
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
-import com.android.app.tracing.coroutines.launchTraced as launch
/**
* This class listens to [SceneTransitionLayout] transitions and manages keyguard transition
@@ -122,11 +121,7 @@
)
override fun start() {
- if (
- communalSceneKtfRefactor() &&
- settingsInteractor.isCommunalFlagEnabled() &&
- !SceneContainerFlag.isEnabled
- ) {
+ if (settingsInteractor.isCommunalFlagEnabled() && !SceneContainerFlag.isEnabled) {
sceneInteractor.registerSceneStateProcessor(this)
listenForSceneTransitionProgress()
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index a4860df..d061a33 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -153,7 +153,7 @@
) {}
/** Called as the UI requests deleting a widget. */
- open fun onDeleteWidget(id: Int, componentName: ComponentName, rank: Int) {}
+ open fun onDeleteWidget(id: Int, key: String, componentName: ComponentName, rank: Int) {}
/** Called as the UI detects a tap event on the widget. */
open fun onTapWidget(componentName: ComponentName, rank: Int) {}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
index 8aba111..59beb1e 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
@@ -141,7 +141,10 @@
metricsLogger.logAddWidget(componentName.flattenToString(), rank)
}
- override fun onDeleteWidget(id: Int, componentName: ComponentName, rank: Int) {
+ override fun onDeleteWidget(id: Int, key: String, componentName: ComponentName, rank: Int) {
+ if (selectedKey.value == key) {
+ setSelectedKey(null)
+ }
communalInteractor.deleteWidget(id)
metricsLogger.logRemoveWidget(componentName.flattenToString(), rank)
}
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 eb96c92..6f5f662 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
@@ -21,7 +21,6 @@
import android.app.DreamManager
import com.android.app.animation.Interpolators
import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.systemui.Flags.communalSceneKtfRefactor
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
@@ -155,9 +154,8 @@
.sample(
communalInteractor.isCommunalAvailable,
communalInteractor.shouldShowCommunal,
- communalSceneInteractor.isIdleOnCommunal,
)
- .collect { (_, isCommunalAvailable, shouldShowCommunal, isIdleOnCommunal) ->
+ .collect { (_, isCommunalAvailable, shouldShowCommunal) ->
val isKeyguardOccludedLegacy = keyguardInteractor.isKeyguardOccluded.value
val primaryBouncerShowing = keyguardInteractor.primaryBouncerShowing.value
val isKeyguardGoingAway = keyguardInteractor.isKeyguardGoingAway.value
@@ -187,10 +185,6 @@
}
} else if (isKeyguardOccludedLegacy) {
startTransitionTo(KeyguardState.OCCLUDED)
- } else if (isIdleOnCommunal && !communalSceneKtfRefactor()) {
- if (!SceneContainerFlag.isEnabled) {
- startTransitionTo(KeyguardState.GLANCEABLE_HUB)
- }
} else if (
shouldTransitionToCommunal(shouldShowCommunal, isCommunalAvailable)
) {
@@ -217,7 +211,6 @@
.sample(
communalInteractor.shouldShowCommunal,
communalInteractor.isCommunalAvailable,
- communalSceneInteractor.isIdleOnCommunal,
keyguardInteractor.biometricUnlockState,
wakeToGoneInteractor.canWakeDirectlyToGone,
keyguardInteractor.primaryBouncerShowing,
@@ -227,7 +220,6 @@
_,
shouldShowCommunal,
isCommunalAvailable,
- isIdleOnCommunal,
biometricUnlockState,
canWakeDirectlyToGone,
primaryBouncerShowing) ->
@@ -252,13 +244,6 @@
ownerReason = "waking from dozing",
)
}
- } else if (isIdleOnCommunal && !communalSceneKtfRefactor()) {
- if (!SceneContainerFlag.isEnabled) {
- startTransitionTo(
- KeyguardState.GLANCEABLE_HUB,
- ownerReason = "waking from dozing",
- )
- }
} else if (
shouldTransitionToCommunal(shouldShowCommunal, isCommunalAvailable)
) {
@@ -276,15 +261,11 @@
}
}
- private suspend fun transitionToGlanceableHub() {
- if (communalSceneKtfRefactor()) {
- communalSceneInteractor.snapToScene(
- newScene = CommunalScenes.Communal,
- loggingReason = "from dozing to hub",
- )
- } else {
- startTransitionTo(KeyguardState.GLANCEABLE_HUB)
- }
+ private fun transitionToGlanceableHub() {
+ communalSceneInteractor.snapToScene(
+ newScene = CommunalScenes.Communal,
+ loggingReason = "from dozing to hub",
+ )
}
/** Dismisses keyguard from the DOZING state. */
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 c1c509b..0fb98ff 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
@@ -21,7 +21,6 @@
import android.app.DreamManager
import com.android.app.animation.Interpolators
import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.systemui.Flags.communalSceneKtfRefactor
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
@@ -59,7 +58,6 @@
@Background bgDispatcher: CoroutineDispatcher,
@Main mainDispatcher: CoroutineDispatcher,
keyguardInteractor: KeyguardInteractor,
- private val glanceableHubTransitions: GlanceableHubTransitions,
private val communalInteractor: CommunalInteractor,
private val communalSceneInteractor: CommunalSceneInteractor,
private val communalSettingsInteractor: CommunalSettingsInteractor,
@@ -87,11 +85,7 @@
listenForDreamingToLockscreenOrGone()
listenForDreamingToAodOrDozing()
listenForTransitionToCamera(scope, keyguardInteractor)
- if (!communalSceneKtfRefactor()) {
- listenForDreamingToGlanceableHub()
- } else {
- listenForDreamingToGlanceableHubFromPowerButton()
- }
+ listenForDreamingToGlanceableHubFromPowerButton()
listenForDreamingToPrimaryBouncer()
}
@@ -105,18 +99,6 @@
}
}
- private fun listenForDreamingToGlanceableHub() {
- if (!communalSettingsInteractor.isCommunalFlagEnabled()) return
- if (SceneContainerFlag.isEnabled) return
- scope.launch("$TAG#listenForDreamingToGlanceableHub", mainDispatcher) {
- glanceableHubTransitions.listenForGlanceableHubTransition(
- transitionOwnerName = TAG,
- fromState = KeyguardState.DREAMING,
- toState = KeyguardState.GLANCEABLE_HUB,
- )
- }
- }
-
/**
* Normally when pressing power button from the dream, the devices goes from DREAMING to DOZING,
* then [FromDozingTransitionInteractor] handles the transition to GLANCEABLE_HUB. However if
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 c5d40a0..2eeba0f 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
@@ -19,7 +19,6 @@
import android.animation.ValueAnimator
import com.android.app.animation.Interpolators
import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.systemui.Flags.communalSceneKtfRefactor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.communal.shared.model.CommunalScenes
@@ -31,12 +30,10 @@
import com.android.systemui.keyguard.KeyguardWmStateRefactor
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
import com.android.systemui.util.kotlin.BooleanFlowOperators.noneOf
-import com.android.systemui.util.kotlin.BooleanFlowOperators.not
import com.android.systemui.util.kotlin.Utils.Companion.sampleFilter
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
@@ -45,10 +42,8 @@
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.FlowPreview
-import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce
-import kotlinx.coroutines.withContext
@OptIn(FlowPreview::class)
@SysUISingleton
@@ -58,7 +53,6 @@
@Background private val scope: CoroutineScope,
@Main mainDispatcher: CoroutineDispatcher,
@Background bgDispatcher: CoroutineDispatcher,
- private val glanceableHubTransitions: GlanceableHubTransitions,
private val communalSettingsInteractor: CommunalSettingsInteractor,
keyguardInteractor: KeyguardInteractor,
private val communalSceneInteractor: CommunalSceneInteractor,
@@ -83,9 +77,6 @@
if (!communalSettingsInteractor.isCommunalFlagEnabled()) {
return
}
- if (!communalSceneKtfRefactor()) {
- listenForHubToLockscreenOrDreaming()
- }
listenForHubToDozing()
listenForHubToPrimaryBouncer()
listenForHubToAlternateBouncer()
@@ -108,30 +99,6 @@
}
}
- /**
- * Listens for the glanceable hub transition to lock screen and directly drives the keyguard
- * transition.
- */
- private fun listenForHubToLockscreenOrDreaming() {
- scope.launch("$TAG#listenForGlanceableHubToLockscreenOrDream") {
- keyguardInteractor.isDreaming.collectLatest { dreaming ->
- withContext(mainDispatcher) {
- val toState =
- if (dreaming) {
- KeyguardState.DREAMING
- } else {
- KeyguardState.LOCKSCREEN
- }
- glanceableHubTransitions.listenForGlanceableHubTransition(
- transitionOwnerName = TAG,
- fromState = KeyguardState.GLANCEABLE_HUB,
- toState = toState,
- )
- }
- }
- }
- }
-
private fun listenForHubToPrimaryBouncer() {
scope.launch("$TAG#listenForHubToPrimaryBouncer") {
keyguardInteractor.primaryBouncerShowing
@@ -161,18 +128,11 @@
powerInteractor.isAsleep
.filterRelevantKeyguardStateAnd { isAsleep -> isAsleep }
.collect {
- if (communalSceneKtfRefactor()) {
- communalSceneInteractor.snapToScene(
- newScene = CommunalScenes.Blank,
- loggingReason = "hub to dozing",
- keyguardState = KeyguardState.DOZING,
- )
- } else {
- startTransitionTo(
- toState = KeyguardState.DOZING,
- modeOnCanceled = TransitionModeOnCanceled.LAST_VALUE,
- )
- }
+ communalSceneInteractor.snapToScene(
+ newScene = CommunalScenes.Blank,
+ loggingReason = "hub to dozing",
+ keyguardState = KeyguardState.DOZING,
+ )
}
}
}
@@ -202,21 +162,17 @@
.filterRelevantKeyguardStateAnd { onTop -> onTop }
.collect {
maybeStartTransitionToOccludedOrInsecureCamera { state, reason ->
- if (communalSceneKtfRefactor()) {
- communalSceneInteractor.changeScene(
- newScene = CommunalScenes.Blank,
- loggingReason = "hub to occluded (KeyguardWmStateRefactor)",
- transitionKey = CommunalTransitionKeys.SimpleFade,
- keyguardState = state,
- )
- null
- } else {
- startTransitionTo(state, ownerReason = reason)
- }
+ communalSceneInteractor.changeScene(
+ newScene = CommunalScenes.Blank,
+ loggingReason = "hub to occluded (KeyguardWmStateRefactor)",
+ transitionKey = CommunalTransitionKeys.SimpleFade,
+ keyguardState = state,
+ )
+ null
}
}
}
- } else if (communalSceneKtfRefactor()) {
+ } else {
scope.launch {
combine(
keyguardInteractor.isKeyguardOccluded,
@@ -248,56 +204,40 @@
)
}
}
- } else {
- scope.launch {
- allOf(keyguardInteractor.isKeyguardOccluded, not(keyguardInteractor.isDreaming))
- .filterRelevantKeyguardStateAnd { isOccludedAndNotDreaming ->
- isOccludedAndNotDreaming
- }
- .collect { _ -> startTransitionTo(KeyguardState.OCCLUDED) }
- }
}
}
private fun listenForHubToGone() {
if (SceneContainerFlag.isEnabled) return
- if (communalSceneKtfRefactor()) {
- scope.launch {
- allOf(
- keyguardInteractor.isKeyguardGoingAway,
- // TODO(b/327225415): Handle edit mode opening here to avoid going to GONE
- // state until after edit mode is ready to be shown.
- noneOf(
- // When launching activities from widgets on the hub, we wait to change
- // scenes until the activity launch is complete.
- communalSceneInteractor.isLaunchingWidget
- ),
- )
- .filterRelevantKeyguardStateAnd { isKeyguardGoingAway -> isKeyguardGoingAway }
- .sample(communalSceneInteractor.editModeState, ::Pair)
- .collect { (_, editModeState) ->
- if (
- editModeState == EditModeState.STARTING ||
- editModeState == EditModeState.SHOWING
- ) {
- // Don't change scenes here as that is handled by the edit activity.
- startTransitionTo(KeyguardState.GONE)
- } else {
- communalSceneInteractor.changeScene(
- newScene = CommunalScenes.Blank,
- loggingReason = "hub to gone",
- transitionKey = CommunalTransitionKeys.SimpleFade,
- keyguardState = KeyguardState.GONE,
- )
- }
+ scope.launch {
+ allOf(
+ keyguardInteractor.isKeyguardGoingAway,
+ // TODO(b/327225415): Handle edit mode opening here to avoid going to GONE
+ // state until after edit mode is ready to be shown.
+ noneOf(
+ // When launching activities from widgets on the hub, we wait to change
+ // scenes until the activity launch is complete.
+ communalSceneInteractor.isLaunchingWidget
+ ),
+ )
+ .filterRelevantKeyguardStateAnd { isKeyguardGoingAway -> isKeyguardGoingAway }
+ .sample(communalSceneInteractor.editModeState, ::Pair)
+ .collect { (_, editModeState) ->
+ if (
+ editModeState == EditModeState.STARTING ||
+ editModeState == EditModeState.SHOWING
+ ) {
+ // Don't change scenes here as that is handled by the edit activity.
+ startTransitionTo(KeyguardState.GONE)
+ } else {
+ communalSceneInteractor.changeScene(
+ newScene = CommunalScenes.Blank,
+ loggingReason = "hub to gone",
+ transitionKey = CommunalTransitionKeys.SimpleFade,
+ keyguardState = KeyguardState.GONE,
+ )
}
- }
- } else {
- scope.launch {
- keyguardInteractor.isKeyguardGoingAway
- .filterRelevantKeyguardStateAnd { isKeyguardGoingAway -> isKeyguardGoingAway }
- .collect { startTransitionTo(KeyguardState.GONE) }
- }
+ }
}
}
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 cf712f1..a01dc02 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
@@ -20,7 +20,6 @@
import android.util.MathUtils
import com.android.app.animation.Interpolators
import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.systemui.Flags.communalSceneKtfRefactor
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
@@ -69,7 +68,6 @@
keyguardInteractor: KeyguardInteractor,
private val shadeRepository: ShadeRepository,
powerInteractor: PowerInteractor,
- private val glanceableHubTransitions: GlanceableHubTransitions,
private val communalSettingsInteractor: CommunalSettingsInteractor,
private val communalInteractor: CommunalInteractor,
private val communalSceneInteractor: CommunalSceneInteractor,
@@ -96,9 +94,6 @@
listenForLockscreenToPrimaryBouncerDragging()
listenForLockscreenToAlternateBouncer()
listenForLockscreenTransitionToCamera()
- if (!communalSceneKtfRefactor()) {
- listenForLockscreenToGlanceableHub()
- }
if (communalSettingsInteractor.isV2FlagEnabled()) {
listenForLockscreenToGlanceableHubV2()
}
@@ -358,24 +353,6 @@
}
}
- /**
- * Listens for transition from glanceable hub back to lock screen and directly drives the
- * keyguard transition.
- */
- private fun listenForLockscreenToGlanceableHub() {
- if (SceneContainerFlag.isEnabled) return
- if (!communalSettingsInteractor.isCommunalFlagEnabled()) {
- return
- }
- scope.launch(context = mainDispatcher) {
- glanceableHubTransitions.listenForGlanceableHubTransition(
- transitionOwnerName = TAG,
- fromState = KeyguardState.LOCKSCREEN,
- toState = KeyguardState.GLANCEABLE_HUB,
- )
- }
- }
-
private fun listenForLockscreenToGlanceableHubV2() {
scope.launch {
communalInteractor.shouldShowCommunal
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 284298d..a2cfdc1 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
@@ -19,7 +19,6 @@
import android.animation.ValueAnimator
import com.android.app.animation.Interpolators
import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.systemui.Flags.communalSceneKtfRefactor
import com.android.systemui.Flags.restartDreamOnUnocclude
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
@@ -143,15 +142,11 @@
startTransitionTo(KeyguardState.DREAMING)
} else if (isIdleOnCommunal || showCommunalFromOccluded) {
if (SceneContainerFlag.isEnabled) return
- if (communalSceneKtfRefactor()) {
- communalSceneInteractor.changeScene(
- newScene = CommunalScenes.Communal,
- loggingReason = "occluded to hub",
- transitionKey = CommunalTransitionKeys.SimpleFade,
- )
- } else {
- startTransitionTo(KeyguardState.GLANCEABLE_HUB)
- }
+ communalSceneInteractor.changeScene(
+ newScene = CommunalScenes.Communal,
+ loggingReason = "occluded to hub",
+ transitionKey = CommunalTransitionKeys.SimpleFade,
+ )
} else {
startTransitionTo(KeyguardState.LOCKSCREEN)
}
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 402c152..30c1aac 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
@@ -18,8 +18,8 @@
import android.animation.ValueAnimator
import android.util.Log
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.keyguard.KeyguardSecurityModel
-import com.android.systemui.Flags.communalSceneKtfRefactor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.dagger.SysUISingleton
@@ -35,7 +35,6 @@
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.kotlin.Utils.Companion.sample
-import com.android.systemui.util.kotlin.sample
import com.android.wm.shell.shared.animation.Interpolators
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -45,7 +44,6 @@
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
-import com.android.app.tracing.coroutines.launchTraced as launch
@SysUISingleton
class FromPrimaryBouncerTransitionInteractor
@@ -173,8 +171,7 @@
// If the hub is showing, and we are not animating a widget launch nor transitioning to
// edit mode, then close the hub immediately.
if (
- communalSceneKtfRefactor() &&
- communalSceneInteractor.isIdleOnCommunal.value &&
+ communalSceneInteractor.isIdleOnCommunal.value &&
!communalSceneInteractor.isLaunchingWidget.value &&
communalSceneInteractor.editModeState.value == null
) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt
deleted file mode 100644
index bde0f56..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt
+++ /dev/null
@@ -1,146 +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.keyguard.domain.interactor
-
-import android.animation.ValueAnimator
-import com.android.app.animation.Interpolators
-import com.android.systemui.communal.domain.interactor.CommunalInteractor
-import com.android.systemui.communal.domain.model.CommunalTransitionProgressModel
-import com.android.systemui.communal.shared.model.CommunalScenes
-import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
-import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.TransitionInfo
-import com.android.systemui.keyguard.shared.model.TransitionState
-import com.android.systemui.scene.shared.flag.SceneContainerFlag
-import com.android.systemui.util.kotlin.sample
-import java.util.UUID
-import javax.inject.Inject
-
-class GlanceableHubTransitions
-@Inject
-constructor(
- private val transitionInteractor: KeyguardTransitionInteractor,
- private val transitionRepository: KeyguardTransitionRepository,
- private val communalInteractor: CommunalInteractor,
-) {
- /**
- * Listens for the glanceable hub transition to the specified scene and directly drives the
- * keyguard transition between the lockscreen and the hub.
- *
- * The glanceable hub transition progress is used as the source of truth as it cannot be driven
- * externally. The progress is used for both transitions caused by user touch input or by
- * programmatic changes.
- */
- suspend fun listenForGlanceableHubTransition(
- transitionOwnerName: String,
- fromState: KeyguardState,
- toState: KeyguardState,
- ) {
- if (SceneContainerFlag.isEnabled) return
- val toScene =
- if (fromState == KeyguardState.GLANCEABLE_HUB) {
- CommunalScenes.Blank
- } else {
- CommunalScenes.Communal
- }
- var transitionId: UUID? = null
-
- communalInteractor
- .transitionProgressToScene(toScene)
- .sample(
- transitionInteractor.startedKeyguardTransitionStep,
- ::Pair,
- )
- .collect { (transitionProgress, lastStartedStep) ->
- val id = transitionId
- if (id == null) {
- // No transition started.
- if (
- transitionProgress is CommunalTransitionProgressModel.Transition &&
- lastStartedStep.to == fromState
- ) {
- transitionId =
- transitionRepository.startTransition(
- TransitionInfo(
- ownerName = transitionOwnerName,
- from = fromState,
- to = toState,
- animator = null, // transition will be manually controlled
- )
- )
- }
- } else {
- if (lastStartedStep.to != toState) {
- return@collect
- }
- // An existing `id` means a transition is started, and calls to
- // `updateTransition` will control it until FINISHED or CANCELED
- val nextState: TransitionState
- val progressFraction: Float
- when (transitionProgress) {
- is CommunalTransitionProgressModel.Idle -> {
- if (transitionProgress.scene == toScene) {
- nextState = TransitionState.FINISHED
- progressFraction = 1f
- } else {
- nextState = TransitionState.CANCELED
- progressFraction = 0f
- }
- }
- is CommunalTransitionProgressModel.Transition -> {
- nextState = TransitionState.RUNNING
- progressFraction = transitionProgress.progress
- }
- is CommunalTransitionProgressModel.OtherTransition -> {
- // Shouldn't happen but if another transition starts during the
- // current one, mark the current one as canceled.
- nextState = TransitionState.CANCELED
- progressFraction = 0f
- }
- }
- transitionRepository.updateTransition(
- id,
- progressFraction,
- nextState,
- )
-
- if (
- nextState == TransitionState.CANCELED ||
- nextState == TransitionState.FINISHED
- ) {
- transitionId = null
- }
-
- // If canceled, just put the state back.
- if (nextState == TransitionState.CANCELED) {
- transitionRepository.startTransition(
- TransitionInfo(
- ownerName = transitionOwnerName,
- from = toState,
- to = fromState,
- animator =
- ValueAnimator().apply {
- interpolator = Interpolators.LINEAR
- duration = 0
- }
- )
- )
- }
- }
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index be814ae..3d2aafe 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -440,6 +440,7 @@
updateEndAreaColor(groupStatus.selected() ? mController.getColorSeekbarProgress()
: mController.getColorItemBackground());
mEndTouchArea.setContentDescription(getDeviceItemContentDescription(device));
+ mCheckBox.setOnCheckedChangeListener(null);
mCheckBox.setChecked(groupStatus.selected());
mCheckBox.setOnCheckedChangeListener(
isEnabled ? (buttonView, isChecked) -> onGroupActionTriggered(
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
index 01bcc24..efdf5be 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
@@ -40,6 +40,7 @@
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.logger.SceneLogger
+import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.ui.composable.Overlay
import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
@@ -218,39 +219,36 @@
* it being a false touch.
*/
fun canChangeScene(toScene: SceneKey): Boolean {
- val interactionTypeOrNull =
- when (toScene) {
- Scenes.Bouncer -> Classifier.BOUNCER_UNLOCK
- Scenes.Gone -> Classifier.UNLOCK
- Scenes.Shade -> Classifier.NOTIFICATION_DRAG_DOWN
- Scenes.QuickSettings -> Classifier.QUICK_SETTINGS
- else -> null
- }
-
- val fromScene = currentScene.value
- val isAllowed =
- interactionTypeOrNull?.let { interactionType ->
- // It's important that the falsing system is always queried, even if no enforcement
- // will occur. This helps build up the right signal in the system.
- val isFalseTouch = falsingInteractor.isFalseTouch(interactionType)
-
- // Only enforce falsing if moving from the lockscreen scene to a new scene.
- val fromLockscreenScene = fromScene == Scenes.Lockscreen
-
- !fromLockscreenScene || !isFalseTouch
- } ?: true
-
- if (isAllowed) {
+ return isInteractionAllowedByFalsing(toScene).also {
// A scene change is guaranteed; log it.
logger.logSceneChanged(
- from = fromScene,
+ from = currentScene.value,
to = toScene,
sceneState = null,
reason = "user interaction",
isInstant = false,
)
}
- return isAllowed
+ }
+
+ /**
+ * Returns `true` if showing the [newlyShown] overlay is currently allowed; `false` otherwise.
+ *
+ * This is invoked only for user-initiated transitions. The goal is to check with the falsing
+ * system whether the overlay change should be rejected due to it being a false touch.
+ */
+ fun canShowOrReplaceOverlay(
+ newlyShown: OverlayKey,
+ beingReplaced: OverlayKey? = null,
+ ): Boolean {
+ return isInteractionAllowedByFalsing(newlyShown).also {
+ // An overlay change is guaranteed; log it.
+ logger.logOverlayChangeRequested(
+ from = beingReplaced,
+ to = newlyShown,
+ reason = "user interaction",
+ )
+ }
}
/**
@@ -313,6 +311,34 @@
return sceneInteractor.filteredUserActions(unfiltered)
}
+ /**
+ * Returns `true` if transitioning to [content] is permissible by the falsing system; `false`
+ * otherwise.
+ */
+ private fun isInteractionAllowedByFalsing(content: ContentKey): Boolean {
+ val interactionTypeOrNull =
+ when (content) {
+ Scenes.Bouncer -> Classifier.BOUNCER_UNLOCK
+ Scenes.Gone -> Classifier.UNLOCK
+ Scenes.Shade,
+ Overlays.NotificationsShade -> Classifier.NOTIFICATION_DRAG_DOWN
+ Scenes.QuickSettings,
+ Overlays.QuickSettingsShade -> Classifier.QUICK_SETTINGS
+ else -> null
+ }
+
+ return interactionTypeOrNull?.let { interactionType ->
+ // It's important that the falsing system is always queried, even if no enforcement
+ // will occur. This helps build up the right signal in the system.
+ val isFalseTouch = falsingInteractor.isFalseTouch(interactionType)
+
+ // Only enforce falsing if moving from the lockscreen scene to new content.
+ val fromLockscreenScene = currentScene.value == Scenes.Lockscreen
+
+ !fromLockscreenScene || !isFalseTouch
+ } ?: true
+ }
+
/** Defines interface for classes that can handle externally-reported [MotionEvent]s. */
interface MotionEventHandler {
/** Notifies that a [MotionEvent] has occurred. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
index 89cb420..9bc5231 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
@@ -69,7 +69,10 @@
messagingStyle.conversationType =
if (entry.ranking.channel.isImportantConversation)
Notification.MessagingStyle.CONVERSATION_TYPE_IMPORTANT
- else Notification.MessagingStyle.CONVERSATION_TYPE_NORMAL
+ else if (entry.ranking.isConversation)
+ Notification.MessagingStyle.CONVERSATION_TYPE_NORMAL
+ else
+ Notification.MessagingStyle.CONVERSATION_TYPE_LEGACY
entry.ranking.conversationShortcutInfo?.let { shortcutInfo ->
logger.logAsyncTaskProgress(entry, "getting shortcut icon")
messagingStyle.shortcutIcon = launcherApps.getShortcutIcon(shortcutInfo)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index a2d563a..9bf0768 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -47,6 +47,7 @@
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.IndentingPrintWriter;
@@ -81,6 +82,8 @@
import com.android.internal.util.ContrastColorUtil;
import com.android.internal.widget.CachingIconView;
import com.android.internal.widget.CallLayout;
+import com.android.internal.widget.ConversationLayout;
+import com.android.internal.widget.MessagingLayout;
import com.android.systemui.Flags;
import com.android.systemui.flags.RefactorFlag;
import com.android.systemui.plugins.FalsingManager;
@@ -97,6 +100,7 @@
import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.FeedbackIcon;
import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
+import com.android.systemui.statusbar.notification.NmSummarizationUiFlag;
import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.NotificationTransitionAnimatorController;
import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -205,6 +209,7 @@
private int mMaxSmallHeightBeforeN;
private int mMaxSmallHeightBeforeP;
private int mMaxSmallHeightBeforeS;
+ private int mMaxSmallHeightWithSummarization;
private int mMaxSmallHeight;
private int mMaxExpandedHeight;
private int mMaxExpandedHeightForPromotedOngoing;
@@ -856,6 +861,8 @@
int smallHeight;
boolean isCallLayout = contractedView instanceof CallLayout;
+ boolean isMessagingLayout = contractedView instanceof MessagingLayout
+ || contractedView instanceof ConversationLayout;
if (customView && beforeS && !mIsSummaryWithChildren) {
if (beforeN) {
@@ -867,6 +874,10 @@
}
} else if (isCallLayout) {
smallHeight = maxExpandedHeight;
+ } else if (NmSummarizationUiFlag.isEnabled()
+ && isMessagingLayout
+ && !TextUtils.isEmpty(mEntry.getRanking().getSummarization())) {
+ smallHeight = mMaxSmallHeightWithSummarization;
} else {
smallHeight = mMaxSmallHeight;
}
@@ -2111,6 +2122,8 @@
mMaxSmallHeight = NotificationUtils.getFontScaledHeight(mContext,
R.dimen.notification_min_height);
}
+ mMaxSmallHeightWithSummarization = NotificationUtils.getFontScaledHeight(mContext,
+ com.android.internal.R.dimen.notification_collapsed_height_with_summarization);
mMaxExpandedHeight = NotificationUtils.getFontScaledHeight(mContext,
R.dimen.notification_max_height);
mMaxExpandedHeightForPromotedOngoing = NotificationUtils.getFontScaledHeight(mContext,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 73e8246..e311b53 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -55,6 +55,7 @@
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
import com.android.systemui.statusbar.notification.InflationException;
+import com.android.systemui.statusbar.notification.NmSummarizationUiFlag;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor;
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiForceExpanded;
@@ -201,13 +202,13 @@
mNotifLayoutInflaterFactoryProvider,
mHeadsUpStyleProvider,
mLogger);
-
result = inflateSmartReplyViews(result, reInflateFlags, entry, row.getContext(),
packageContext, row.getExistingSmartReplyState(), smartRepliesInflater, mLogger);
boolean isConversation = entry.getRanking().isConversation();
Notification.MessagingStyle messagingStyle = null;
- if (isConversation && (AsyncHybridViewInflation.isEnabled()
- || LockscreenOtpRedaction.isSingleLineViewEnabled())) {
+ if (NmSummarizationUiFlag.isEnabled()
+ || (isConversation && (AsyncHybridViewInflation.isEnabled()
+ || LockscreenOtpRedaction.isSingleLineViewEnabled()))) {
messagingStyle = mConversationProcessor
.processNotification(entry, builder, mLogger);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
index 589e5b8..517fc3a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
@@ -48,6 +48,7 @@
import com.android.systemui.statusbar.NotificationRemoteInputManager
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor
import com.android.systemui.statusbar.notification.InflationException
+import com.android.systemui.statusbar.notification.NmSummarizationUiFlag
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
@@ -700,7 +701,7 @@
// process conversations and extract the messaging style
val messagingStyle =
- if (entry.ranking.isConversation) {
+ if (NmSummarizationUiFlag.isEnabled || entry.ranking.isConversation) {
conversationProcessor.processNotification(entry, builder, logger)
} else null
@@ -730,9 +731,8 @@
builder = builder,
systemUiContext = systemUiContext,
redactText = false,
- summarization = entry.sbn.notification.extras.getCharSequence(
- EXTRA_SUMMARIZED_CONTENT,
- )
+ summarization =
+ entry.sbn.notification.extras.getCharSequence(EXTRA_SUMMARIZED_CONTENT),
)
} else null
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 d995b86..0f7366d 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
@@ -38,7 +38,6 @@
bgDispatcher = testDispatcher,
mainDispatcher = testDispatcher,
keyguardInteractor = keyguardInteractor,
- glanceableHubTransitions = glanceableHubTransitions,
communalInteractor = communalInteractor,
communalSceneInteractor = communalSceneInteractor,
communalSettingsInteractor = communalSettingsInteractor,
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 494f08b..bf72e48 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
@@ -31,7 +31,6 @@
scope = applicationCoroutineScope,
mainDispatcher = testDispatcher,
bgDispatcher = testDispatcher,
- glanceableHubTransitions = glanceableHubTransitions,
communalSettingsInteractor = communalSettingsInteractor,
keyguardInteractor = keyguardInteractor,
transitionRepository = keyguardTransitionRepository,
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 ff7a06c..985044c 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
@@ -39,7 +39,6 @@
keyguardInteractor = keyguardInteractor,
shadeRepository = shadeRepository,
powerInteractor = powerInteractor,
- glanceableHubTransitions = glanceableHubTransitions,
communalSettingsInteractor = communalSettingsInteractor,
swipeToDismissInteractor = swipeToDismissInteractor,
keyguardOcclusionInteractor = keyguardOcclusionInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitionsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitionsKosmos.kt
deleted file mode 100644
index a45b269..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitionsKosmos.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyguard.domain.interactor
-
-import com.android.systemui.communal.domain.interactor.communalInteractor
-import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
-import com.android.systemui.kosmos.Kosmos
-
-val Kosmos.glanceableHubTransitions by
- Kosmos.Fixture {
- GlanceableHubTransitions(
- transitionRepository = keyguardTransitionRepository,
- transitionInteractor = keyguardTransitionInteractor,
- communalInteractor = communalInteractor,
- )
- }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/ConversationNotificationManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/ConversationNotificationManagerKosmos.kt
new file mode 100644
index 0000000..af53ae56
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/ConversationNotificationManagerKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.conversationNotificationManager: ConversationNotificationManager by
+ Kosmos.Fixture { mock<ConversationNotificationManager>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderLoggerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderLoggerKosmos.kt
new file mode 100644
index 0000000..cc40222
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderLoggerKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.notificationRowContentBinderLogger: NotificationRowContentBinderLogger by
+ Kosmos.Fixture { mock<NotificationRowContentBinderLogger>() }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index b48d0a6..86871ea 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -12598,7 +12598,7 @@
//==========================================================================================
static final int LOG_NB_EVENTS_LIFECYCLE = 20;
static final int LOG_NB_EVENTS_PHONE_STATE = 20;
- static final int LOG_NB_EVENTS_DEVICE_CONNECTION = 50;
+ static final int LOG_NB_EVENTS_DEVICE_CONNECTION = 200;
static final int LOG_NB_EVENTS_FORCE_USE = 20;
static final int LOG_NB_EVENTS_VOLUME = 100;
static final int LOG_NB_EVENTS_DYN_POLICY = 10;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 34f8ac6..191c21e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -8288,8 +8288,11 @@
Preconditions.checkCallAuthorization(isSystemUid(caller));
// Managed Profile password can only be changed when it has a separate challenge.
if (!isSeparateProfileChallengeEnabled(userId)) {
- Preconditions.checkCallAuthorization(!isManagedProfile(userId), "You can "
- + "not set the active password for a managed profile, userId = %d", userId);
+ if (isManagedProfile(userId)) {
+ Slogf.i(LOG_TAG, "You can not set the active password for a managed profile,"
+ + " userId = %d", userId);
+ return;
+ }
}
DevicePolicyData policy = getUserData(userId);
diff --git a/tests/testables/src/android/testing/TestableLooper.java b/tests/testables/src/android/testing/TestableLooper.java
index 3ee6dc4..1273826 100644
--- a/tests/testables/src/android/testing/TestableLooper.java
+++ b/tests/testables/src/android/testing/TestableLooper.java
@@ -16,6 +16,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
@@ -24,7 +25,7 @@
import android.os.TestLooperManager;
import android.util.ArrayMap;
-import androidx.test.InstrumentationRegistry;
+import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.runners.model.FrameworkMethod;
@@ -33,8 +34,11 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayDeque;
import java.util.Map;
import java.util.Objects;
+import java.util.Queue;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -67,16 +71,38 @@
private Handler mHandler;
private TestLooperManager mQueueWrapper;
+ /**
+ * Baklava introduces new {@link TestLooperManager} APIs that we can use instead of reflection.
+ */
+ private static boolean isAtLeastBaklava() {
+ Method[] methods = TestLooperManager.class.getMethods();
+ for (Method method : methods) {
+ if (method.getName().equals("peekWhen")) {
+ return true;
+ }
+ }
+ return false;
+ // TODO(shayba): delete the above, uncomment the below.
+ // SDK_INT has not yet ramped to Baklava in all 25Q2 builds.
+ // return Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA;
+ }
+
static {
- try {
- MESSAGE_QUEUE_MESSAGES_FIELD = MessageQueue.class.getDeclaredField("mMessages");
- MESSAGE_QUEUE_MESSAGES_FIELD.setAccessible(true);
- MESSAGE_NEXT_FIELD = Message.class.getDeclaredField("next");
- MESSAGE_NEXT_FIELD.setAccessible(true);
- MESSAGE_WHEN_FIELD = Message.class.getDeclaredField("when");
- MESSAGE_WHEN_FIELD.setAccessible(true);
- } catch (NoSuchFieldException e) {
- throw new RuntimeException("Failed to initialize TestableLooper", e);
+ if (isAtLeastBaklava()) {
+ MESSAGE_QUEUE_MESSAGES_FIELD = null;
+ MESSAGE_NEXT_FIELD = null;
+ MESSAGE_WHEN_FIELD = null;
+ } else {
+ try {
+ MESSAGE_QUEUE_MESSAGES_FIELD = MessageQueue.class.getDeclaredField("mMessages");
+ MESSAGE_QUEUE_MESSAGES_FIELD.setAccessible(true);
+ MESSAGE_NEXT_FIELD = Message.class.getDeclaredField("next");
+ MESSAGE_NEXT_FIELD.setAccessible(true);
+ MESSAGE_WHEN_FIELD = Message.class.getDeclaredField("when");
+ MESSAGE_WHEN_FIELD.setAccessible(true);
+ } catch (NoSuchFieldException e) {
+ throw new RuntimeException("Failed to initialize TestableLooper", e);
+ }
}
}
@@ -222,8 +248,61 @@
}
public void moveTimeForward(long milliSeconds) {
+ if (isAtLeastBaklava()) {
+ moveTimeForwardBaklava(milliSeconds);
+ } else {
+ moveTimeForwardLegacy(milliSeconds);
+ }
+ }
+
+ private void moveTimeForwardBaklava(long milliSeconds) {
+ // Drain all Messages from the queue.
+ Queue<Message> messages = new ArrayDeque<>();
+ while (true) {
+ Message message = mQueueWrapper.poll();
+ if (message == null) {
+ break;
+ }
+
+ // Adjust the Message's delivery time.
+ long newWhen = message.when - milliSeconds;
+ if (newWhen < 0) {
+ newWhen = 0;
+ }
+ message.when = newWhen;
+ messages.add(message);
+ }
+
+ // Repost all Messages back to the queuewith a new time.
+ while (true) {
+ Message message = messages.poll();
+ if (message == null) {
+ break;
+ }
+
+ Runnable callback = message.getCallback();
+ Handler handler = message.getTarget();
+ long when = message.getWhen();
+
+ // The Message cannot be re-enqueued because it is marked in use.
+ // Make a copy of the Message and recycle the original.
+ // This resets {@link Message#isInUse()} but retains all other content.
+ {
+ Message newMessage = Message.obtain();
+ newMessage.copyFrom(message);
+ newMessage.setCallback(callback);
+ mQueueWrapper.recycle(message);
+ message = newMessage;
+ }
+
+ // Send the Message back to its Handler to be re-enqueued.
+ handler.sendMessageAtTime(message, when);
+ }
+ }
+
+ private void moveTimeForwardLegacy(long milliSeconds) {
try {
- Message msg = getMessageLinkedList();
+ Message msg = (Message) MESSAGE_QUEUE_MESSAGES_FIELD.get(mLooper.getQueue());
while (msg != null) {
long updatedWhen = msg.getWhen() - milliSeconds;
if (updatedWhen < 0) {
@@ -237,17 +316,6 @@
}
}
- private Message getMessageLinkedList() {
- try {
- MessageQueue queue = mLooper.getQueue();
- return (Message) MESSAGE_QUEUE_MESSAGES_FIELD.get(queue);
- } catch (IllegalAccessException e) {
- throw new RuntimeException(
- "Access failed in TestableLooper: get - MessageQueue.mMessages",
- e);
- }
- }
-
private int processQueuedMessages() {
int count = 0;
Runnable barrierRunnable = () -> { };