Merge "Modify minimize flicker test assertions." into main
diff --git a/apct-tests/perftests/core/src/android/os/TracePerfTest.java b/apct-tests/perftests/core/src/android/os/TracePerfTest.java
index d905124..00e1c1f 100644
--- a/apct-tests/perftests/core/src/android/os/TracePerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/TracePerfTest.java
@@ -147,7 +147,7 @@
.addField(1 /* sending_thread_name */, "foo")
.endNested()
.endProto()
- .addTerminatingFlow(5)
+ .setTerminatingFlow(5)
.emit();
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
@@ -158,7 +158,7 @@
.addField(1 /* sending_thread_name */, "foo")
.endNested()
.endProto()
- .addTerminatingFlow(5)
+ .setTerminatingFlow(5)
.emit();
}
}
diff --git a/apct-tests/perftests/healthconnect/OWNERS b/apct-tests/perftests/healthconnect/OWNERS
index acfe799..7c9e769 100644
--- a/apct-tests/perftests/healthconnect/OWNERS
+++ b/apct-tests/perftests/healthconnect/OWNERS
@@ -1,5 +1,4 @@
# Bug component: 1219472
-arkivanov@google.com
jstembridge@google.com
itsleo@google.com
diff --git a/api/OWNERS b/api/OWNERS
index f2bcf13..31ffa8c 100644
--- a/api/OWNERS
+++ b/api/OWNERS
@@ -1,4 +1,3 @@
-hansson@google.com
# Modularization team
file:platform/packages/modules/common:/OWNERS
diff --git a/cmds/idmap2/OWNERS b/cmds/idmap2/OWNERS
index 062ffd4..def9f40 100644
--- a/cmds/idmap2/OWNERS
+++ b/cmds/idmap2/OWNERS
@@ -1,4 +1,3 @@
set noparent
-toddke@google.com
patb@google.com
zyy@google.com
diff --git a/cmds/incidentd/OWNERS b/cmds/incidentd/OWNERS
index bcdcfc3..db8fa9f 100644
--- a/cmds/incidentd/OWNERS
+++ b/cmds/incidentd/OWNERS
@@ -1,4 +1,3 @@
joeo@google.com
yaochen@google.com
yanmin@google.com
-zhouwenjie@google.com
diff --git a/core/api/current.txt b/core/api/current.txt
index 1312753..3da5a5c 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -16993,7 +16993,7 @@
method public void setFilterBitmap(boolean);
method public void setFlags(int);
method public void setFontFeatureSettings(String);
- method @FlaggedApi("com.android.text.flags.typeface_redesign_readonly") public boolean setFontVariationOverride(@Nullable String);
+ method @FlaggedApi("com.android.text.flags.typeface_redesign_readonly") public void setFontVariationOverride(@Nullable String);
method public boolean setFontVariationSettings(String);
method public void setHinting(int);
method public void setLetterSpacing(float);
@@ -23332,12 +23332,12 @@
public static final class MediaCodecInfo.CodecCapabilities {
ctor public MediaCodecInfo.CodecCapabilities();
method public static android.media.MediaCodecInfo.CodecCapabilities createFromProfileLevel(String, int, int);
- method public android.media.MediaCodecInfo.AudioCapabilities getAudioCapabilities();
+ method @Nullable public android.media.MediaCodecInfo.AudioCapabilities getAudioCapabilities();
method public android.media.MediaFormat getDefaultFormat();
- method public android.media.MediaCodecInfo.EncoderCapabilities getEncoderCapabilities();
+ method @Nullable public android.media.MediaCodecInfo.EncoderCapabilities getEncoderCapabilities();
method public int getMaxSupportedInstances();
method public String getMimeType();
- method public android.media.MediaCodecInfo.VideoCapabilities getVideoCapabilities();
+ method @Nullable public android.media.MediaCodecInfo.VideoCapabilities getVideoCapabilities();
method public boolean isFeatureRequired(String);
method public boolean isFeatureSupported(String);
method public boolean isFormatSupported(android.media.MediaFormat);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 514a582..00ec48b 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2128,6 +2128,12 @@
method public android.media.PlaybackParams setAudioStretchMode(int);
}
+ public class Utils {
+ ctor public Utils();
+ field public static final String SYNCHRONIZED_VIBRATION = "synchronized";
+ field public static final String VIBRATION_URI_PARAM = "vibration_uri";
+ }
+
public final class VolumePolicy implements android.os.Parcelable {
ctor public VolumePolicy(boolean, boolean, boolean, int);
method public int describeContents();
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 69d3e8d..f9ec214 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -550,35 +550,35 @@
public static final int START_ASSISTANT_NOT_ACTIVE_SESSION = FIRST_START_FATAL_ERROR_CODE + 11;
/**
- * Result for IActivityManaqer.startActivity: the activity was started
+ * Result for IActivityManager.startActivity: the activity was started
* successfully as normal.
* @hide
*/
public static final int START_SUCCESS = FIRST_START_SUCCESS_CODE;
/**
- * Result for IActivityManaqer.startActivity: the caller asked that the Intent not
+ * Result for IActivityManager.startActivity: the caller asked that the Intent not
* be executed if it is the recipient, and that is indeed the case.
* @hide
*/
public static final int START_RETURN_INTENT_TO_CALLER = FIRST_START_SUCCESS_CODE + 1;
/**
- * Result for IActivityManaqer.startActivity: activity was started or brought forward in an
+ * Result for IActivityManager.startActivity: activity was started or brought forward in an
* existing task which was brought to the foreground.
* @hide
*/
public static final int START_TASK_TO_FRONT = FIRST_START_SUCCESS_CODE + 2;
/**
- * Result for IActivityManaqer.startActivity: activity wasn't really started, but
+ * Result for IActivityManager.startActivity: activity wasn't really started, but
* the given Intent was given to the existing top activity.
* @hide
*/
public static final int START_DELIVERED_TO_TOP = FIRST_START_SUCCESS_CODE + 3;
/**
- * Result for IActivityManaqer.startActivity: request was canceled because
+ * Result for IActivityManager.startActivity: request was canceled because
* app switches are temporarily canceled to ensure the user's last request
* (such as pressing home) is performed.
* @hide
@@ -586,7 +586,7 @@
public static final int START_SWITCHES_CANCELED = FIRST_START_NON_FATAL_ERROR_CODE;
/**
- * Result for IActivityManaqer.startActivity: a new activity was attempted to be started
+ * Result for IActivityManager.startActivity: a new activity was attempted to be started
* while in Lock Task Mode.
* @hide
*/
@@ -594,55 +594,55 @@
FIRST_START_NON_FATAL_ERROR_CODE + 1;
/**
- * Result for IActivityManaqer.startActivity: a new activity start was aborted. Never returned
+ * Result for IActivityManager.startActivity: a new activity start was aborted. Never returned
* externally.
* @hide
*/
public static final int START_ABORTED = FIRST_START_NON_FATAL_ERROR_CODE + 2;
/**
- * Flag for IActivityManaqer.startActivity: do special start mode where
+ * Flag for IActivityManager.startActivity: do special start mode where
* a new activity is launched only if it is needed.
* @hide
*/
public static final int START_FLAG_ONLY_IF_NEEDED = 1<<0;
/**
- * Flag for IActivityManaqer.startActivity: launch the app for
+ * Flag for IActivityManager.startActivity: launch the app for
* debugging.
* @hide
*/
public static final int START_FLAG_DEBUG = 1<<1;
/**
- * Flag for IActivityManaqer.startActivity: launch the app for
+ * Flag for IActivityManager.startActivity: launch the app for
* allocation tracking.
* @hide
*/
public static final int START_FLAG_TRACK_ALLOCATION = 1<<2;
/**
- * Flag for IActivityManaqer.startActivity: launch the app with
+ * Flag for IActivityManager.startActivity: launch the app with
* native debugging support.
* @hide
*/
public static final int START_FLAG_NATIVE_DEBUGGING = 1<<3;
/**
- * Flag for IActivityManaqer.startActivity: launch the app for
+ * Flag for IActivityManager.startActivity: launch the app for
* debugging and suspend threads.
* @hide
*/
public static final int START_FLAG_DEBUG_SUSPEND = 1 << 4;
/**
- * Result for IActivityManaqer.broadcastIntent: success!
+ * Result for IActivityManager.broadcastIntent: success!
* @hide
*/
public static final int BROADCAST_SUCCESS = 0;
/**
- * Result for IActivityManaqer.broadcastIntent: attempt to broadcast
+ * Result for IActivityManager.broadcastIntent: attempt to broadcast
* a sticky intent without appropriate permission.
* @hide
*/
@@ -656,20 +656,20 @@
public static final int BROADCAST_FAILED_USER_STOPPED = -2;
/**
- * Type for IActivityManaqer.getIntentSender: this PendingIntent type is unknown.
+ * Type for IActivityManager.getIntentSender: this PendingIntent type is unknown.
* @hide
*/
public static final int INTENT_SENDER_UNKNOWN = 0;
/**
- * Type for IActivityManaqer.getIntentSender: this PendingIntent is
+ * Type for IActivityManager.getIntentSender: this PendingIntent is
* for a sendBroadcast operation.
* @hide
*/
public static final int INTENT_SENDER_BROADCAST = 1;
/**
- * Type for IActivityManaqer.getIntentSender: this PendingIntent is
+ * Type for IActivityManager.getIntentSender: this PendingIntent is
* for a startActivity operation.
* @hide
*/
@@ -677,21 +677,21 @@
public static final int INTENT_SENDER_ACTIVITY = 2;
/**
- * Type for IActivityManaqer.getIntentSender: this PendingIntent is
+ * Type for IActivityManager.getIntentSender: this PendingIntent is
* for an activity result operation.
* @hide
*/
public static final int INTENT_SENDER_ACTIVITY_RESULT = 3;
/**
- * Type for IActivityManaqer.getIntentSender: this PendingIntent is
+ * Type for IActivityManager.getIntentSender: this PendingIntent is
* for a startService operation.
* @hide
*/
public static final int INTENT_SENDER_SERVICE = 4;
/**
- * Type for IActivityManaqer.getIntentSender: this PendingIntent is
+ * Type for IActivityManager.getIntentSender: this PendingIntent is
* for a startForegroundService operation.
* @hide
*/
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 1a6e9b0..dc5974f 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -22,14 +22,12 @@
import static android.permission.flags.Flags.shouldRegisterAttributionSource;
import static android.view.WindowManager.LayoutParams.WindowType;
-import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.UiContext;
-import android.companion.virtual.VirtualDevice;
import android.companion.virtual.VirtualDeviceManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.AttributionSource;
@@ -2367,47 +2365,11 @@
Log.v(TAG, "Treating renounced permission " + permission + " as denied");
return PERMISSION_DENIED;
}
- int deviceId = resolveDeviceIdForPermissionCheck(permission);
+ int deviceId = PermissionManager.resolveDeviceIdForPermissionCheck(this, getDeviceId(),
+ permission);
return PermissionManager.checkPermission(permission, pid, uid, deviceId);
}
- private int resolveDeviceIdForPermissionCheck(String permission) {
- // When checking a device-aware permission on a remote device, if the permission is CAMERA
- // or RECORD_AUDIO we need to check remote device's corresponding capability. If the remote
- // device doesn't have capability fall back to checking permission on the default device.
- // Note: we only perform permission check redirection when the device id is not explicitly
- // set in the context.
- int deviceId = getDeviceId();
- if (deviceId != Context.DEVICE_ID_DEFAULT
- && !mIsExplicitDeviceId
- && PermissionManager.DEVICE_AWARE_PERMISSIONS.contains(permission)) {
- VirtualDeviceManager virtualDeviceManager =
- getSystemService(VirtualDeviceManager.class);
- if (virtualDeviceManager == null) {
- Slog.e(
- TAG,
- "VDM is not enabled when device id is not default. deviceId = "
- + deviceId);
- } else {
- VirtualDevice virtualDevice = virtualDeviceManager.getVirtualDevice(deviceId);
- if (virtualDevice != null) {
- if ((Objects.equals(permission, Manifest.permission.RECORD_AUDIO)
- && !virtualDevice.hasCustomAudioInputSupport())
- || (Objects.equals(permission, Manifest.permission.CAMERA)
- && !virtualDevice.hasCustomCameraSupport())) {
- deviceId = Context.DEVICE_ID_DEFAULT;
- }
- } else {
- Slog.e(
- TAG,
- "virtualDevice is not found when device id is not default. deviceId = "
- + deviceId);
- }
- }
- }
- return deviceId;
- }
-
/** @hide */
@Override
public int checkPermission(String permission, int pid, int uid, IBinder callerToken) {
@@ -2511,7 +2473,8 @@
@Override
public int getPermissionRequestState(String permission) {
Objects.requireNonNull(permission, "Permission name can't be null");
- int deviceId = resolveDeviceIdForPermissionCheck(permission);
+ int deviceId = PermissionManager.resolveDeviceIdForPermissionCheck(this, getDeviceId(),
+ permission);
PermissionManager permissionManager = getSystemService(PermissionManager.class);
return permissionManager.getPermissionRequestState(getOpPackageName(), permission,
deviceId);
diff --git a/core/java/android/app/DreamManager.java b/core/java/android/app/DreamManager.java
index c597a9d..555006b 100644
--- a/core/java/android/app/DreamManager.java
+++ b/core/java/android/app/DreamManager.java
@@ -71,8 +71,11 @@
@TestApi
@RequiresPermission(WRITE_SECURE_SETTINGS)
public void setScreensaverEnabled(boolean enabled) {
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.SCREENSAVER_ENABLED, enabled ? 1 : 0, UserHandle.USER_CURRENT);
+ try {
+ mService.setScreensaverEnabled(enabled);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
}
/**
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 1b71e73..cc72d8f 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -9526,14 +9526,21 @@
.viewType(viewType)
.highlightExpander(isConversationLayout)
.hideProgress(true)
- .text(null)
.hideLeftIcon(isOneToOne)
.hideRightIcon(hideRightIcons || isOneToOne);
if (notificationsRedesignTemplates()) {
+ String lastMessage = !mMessages.isEmpty()
+ ? mMessages.getLast().mText.toString() : null;
+
p.title(conversationTitle)
+ // The text is not actually displayed like this (since we're using a
+ // MessagingLinearLayout instead of the regular text), but we're using it to
+ // know whether the notification will have a second line in practice.
+ .text(lastMessage)
.hideAppName(isCollapsed);
} else {
p.title(isLegacyHeaderless ? conversationTitle : null)
+ .text(null)
.headerTextSecondary(isLegacyHeaderless ? null : conversationTitle);
}
RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index c573161..38141cf 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -293,6 +293,7 @@
// The test mode. This is only used to ensure that the test functions setTestMode() and
// testPropertyName() are used correctly.
+ @GuardedBy("sGlobalLock")
private static boolean sTestMode = false;
/**
@@ -668,7 +669,7 @@
// True if this handler is in test mode. If it is in test mode, then nonces are stored
// and retrieved from mTestNonce.
@GuardedBy("mLock")
- private boolean mTestMode = false;
+ private boolean mTestMode;
// This is the local value of the nonce, as last set by the NonceHandler. It is always
// updated by the setNonce() operation. The getNonce() operation returns this value in
@@ -692,6 +693,9 @@
NonceHandler(@NonNull String name) {
mName = name;
+ synchronized (sGlobalLock) {
+ mTestMode = sTestMode;
+ }
}
/**
@@ -1414,9 +1418,13 @@
/**
* Enable or disable testing. The protocol requires that the mode toggle: for instance, it is
- * illegal to clear the test mode if the test mode is already off. The purpose is solely to
- * ensure that test clients do not forget to use the test mode properly, even though the
- * current logic does not care.
+ * illegal to clear the test mode if the test mode is already off. Enabling test mode puts
+ * all caches in the process into test mode; all nonces are initialized to UNSET and
+ * subsequent reads and writes are to process memory. This has the effect of disabling all
+ * caches that are not local to the process. Disabling test mode restores caches to normal
+ * operation.
+ * @param mode The desired test mode.
+ * @throws IllegalStateException if the supplied mode is already set.
* @hide
*/
@VisibleForTesting
@@ -1431,10 +1439,8 @@
}
}
sTestMode = mode;
- if (mode) {
- // No action when testing begins.
- } else {
- resetAfterTestLocked();
+ if (Flags.picTestMode() || !mode) {
+ setTestModeLocked(mode);
}
}
}
@@ -1445,11 +1451,11 @@
* that were not originally in test mode.
*/
@GuardedBy("sGlobalLock")
- private static void resetAfterTestLocked() {
+ private static void setTestModeLocked(boolean mode) {
for (Iterator<String> e = sHandlers.keys().asIterator(); e.hasNext(); ) {
String s = e.next();
final NonceHandler h = sHandlers.get(s);
- h.setTestMode(false);
+ h.setTestMode(mode);
}
}
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index e93d8bdb..3a02188 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -169,6 +169,23 @@
"name": "CtsWindowManagerBackgroundActivityTestCases"
}
],
+ // v2/sysui/suite/test-mapping-sysui-screenshot-test
+ "sysui-screenshot-test": [
+ {
+ "name": "SystemUIGoogleScreenshotTests",
+ "options": [
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "android.platform.test.annotations.Postsubmit"
+ }
+ ]
+ }
+ ],
"postsubmit": [
{
"file_patterns": ["(/|^)ActivityThreadClientTest.java"],
diff --git a/core/java/android/app/contentsuggestions/OWNERS b/core/java/android/app/contentsuggestions/OWNERS
index 5f8de77..3d21a6a 100644
--- a/core/java/android/app/contentsuggestions/OWNERS
+++ b/core/java/android/app/contentsuggestions/OWNERS
@@ -1,4 +1,3 @@
# Bug component: 643919
hackz@google.com
-volnov@google.com
diff --git a/core/java/android/app/contextualsearch/flags.aconfig b/core/java/android/app/contextualsearch/flags.aconfig
index 1de034b..1a14b20 100644
--- a/core/java/android/app/contextualsearch/flags.aconfig
+++ b/core/java/android/app/contextualsearch/flags.aconfig
@@ -23,6 +23,18 @@
bug: "371065456"
}
+
+flag {
+ name: "report_secure_surfaces_in_assist_structure"
+ namespace: "windowing_frontend"
+ description: "SurfaceView reports when the surface is using a SECURE flag."
+ bug: "390504528"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+
flag {
name: "contextual_search_prevent_self_capture"
namespace: "sysui_integrations"
diff --git a/core/java/android/app/people/OWNERS b/core/java/android/app/people/OWNERS
index 7371a88..399511a 100644
--- a/core/java/android/app/people/OWNERS
+++ b/core/java/android/app/people/OWNERS
@@ -1,4 +1,3 @@
# Bug component: 978868
-danningc@google.com
-juliacr@google.com
\ No newline at end of file
+juliacr@google.com
diff --git a/core/java/android/app/performance.aconfig b/core/java/android/app/performance.aconfig
index 2569f7b..82875eb 100644
--- a/core/java/android/app/performance.aconfig
+++ b/core/java/android/app/performance.aconfig
@@ -50,3 +50,10 @@
description: "Cache null returns from binder calls"
bug: "372923336"
}
+
+flag {
+ namespace: "system_performance"
+ name: "pic_test_mode"
+ description: "Updated test mode for PIC"
+ bug: "396173886"
+}
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index 6e3e86c..e726bc9 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -236,9 +236,17 @@
* 2. The transaction message is scheduled.
* 3. The client calls {@link TransactionExecutor#execute(ClientTransaction)}, which executes
* all callbacks and necessary lifecycle transitions.
+ *
+ * @return {@link RemoteException} if the transaction failed.
*/
- public void schedule() throws RemoteException {
- mClient.scheduleTransaction(this);
+ @Nullable
+ public RemoteException schedule() {
+ try {
+ mClient.scheduleTransaction(this);
+ return null;
+ } catch (RemoteException e) {
+ return e;
+ }
}
// Parcelable implementation
diff --git a/core/java/android/app/wearable/OWNERS b/core/java/android/app/wearable/OWNERS
index 497eaf0..56c8ca5 100644
--- a/core/java/android/app/wearable/OWNERS
+++ b/core/java/android/app/wearable/OWNERS
@@ -2,4 +2,3 @@
hackz@google.com
oni@google.com
tomchan@google.com
-volnov@google.com
\ No newline at end of file
diff --git a/core/java/android/content/integrity/OWNERS b/core/java/android/content/integrity/OWNERS
index 20c758a..ca65fda 100644
--- a/core/java/android/content/integrity/OWNERS
+++ b/core/java/android/content/integrity/OWNERS
@@ -1,5 +1,4 @@
# Bug component: 722021
toddke@android.com
-toddke@google.com
patb@google.com
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index e645060..fd59ea9 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1347,6 +1347,33 @@
314961188L;
/**
+ * Excludes the packages the override is applied to from the camera compatibility treatment for
+ * fixed-orientation apps, which simulates running on a portrait device, in the orientation
+ * requested by the app.
+ *
+ * <p>This treatment aims to mitigate camera issues on large screens, like stretched or sideways
+ * previews. It simulates running on a portrait device by:
+ * <ul>
+ * <li>Letterboxing the app window,
+ * <li>Cropping the camera buffer to match the app's requested orientation,
+ * <li>Setting the camera sensor orientation to portrait.
+ * <li>Setting the display rotation to match the app's requested orientation, given portrait
+ * natural orientation,
+ * <li>Refreshes the activity to trigger new camera setup, with sandboxed values.
+ * </ul>
+ *
+ * <p>By setting this override to {@code true}, it disables the camera compatibility treatment
+ * which simulates app's requested orientation.
+ *
+ * @hide
+ */
+ @ChangeId
+ @Overridable
+ @Disabled
+ public static final long OVERRIDE_CAMERA_COMPAT_DISABLE_SIMULATE_REQUESTED_ORIENTATION =
+ 398195815L; // buganizer id
+
+ /**
* This change id forces the packages it is applied to sandbox {@link android.view.View} API to
* an activity bounds for:
*
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 9e91f59..49fd634 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -5000,10 +5000,9 @@
* supports the Android XR Spatial APIs. The feature version indicates the highest version of
* the Android XR Spatial APIs supported by the device.
*
- * <p>Also see <a href="https://developer.android.com/xr">Getting started with Spatializing
- * your app</a>.
+ * <p>Also see <a href="https://developer.android.com/develop/xr">Develop with the Android XR
+ * SDK</a>.
*/
- // TODO(b/374330735): update public documentation once link content is finalized
@FlaggedApi(android.xr.Flags.FLAG_XR_MANIFEST_ENTRIES)
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_XR_API_SPATIAL =
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 255a08c..42bef0e 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -400,3 +400,11 @@
bug: "378539511"
is_fixed_read_only: true
}
+
+flag {
+ name: "cloud_compilation_verification"
+ namespace: "art_mainline"
+ description: "Feature flag to enable the Cloud Compilation install-time verification in the package manager."
+ bug: "377474232"
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/content/pm/parsing/OWNERS b/core/java/android/content/pm/parsing/OWNERS
index 8049d5c..445a833 100644
--- a/core/java/android/content/pm/parsing/OWNERS
+++ b/core/java/android/content/pm/parsing/OWNERS
@@ -2,4 +2,3 @@
chiuwinson@google.com
patb@google.com
-toddke@google.com
diff --git a/core/java/android/content/pm/permission/OWNERS b/core/java/android/content/pm/permission/OWNERS
index cf7e689..8ef8474 100644
--- a/core/java/android/content/pm/permission/OWNERS
+++ b/core/java/android/content/pm/permission/OWNERS
@@ -3,6 +3,5 @@
include platform/frameworks/base:/core/java/android/permission/OWNERS
toddke@android.com
-toddke@google.com
patb@google.com
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 1e6469c..0ceafa0 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1515,7 +1515,7 @@
VERSION_CODES.VANILLA_ICE_CREAM * SDK_INT_MULTIPLIER;
/**
- * The upcoming, not yet finalized, version of Android.
+ * Android 36.0.
*/
public static final int BAKLAVA = VERSION_CODES.BAKLAVA * SDK_INT_MULTIPLIER;
}
diff --git a/core/java/android/os/CombinedMessageQueue/MessageQueue.java b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
index 7497234..ce1717b 100644
--- a/core/java/android/os/CombinedMessageQueue/MessageQueue.java
+++ b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
@@ -92,11 +92,8 @@
// queue for async messages when inserting a message at the tail.
private int mAsyncMessageCount;
- /**
- * @hide
- */
private final AtomicLong mMessageCount = new AtomicLong();
- private final Thread mThread;
+ private final String mThreadName;
private final long mTid;
/**
@@ -133,7 +130,7 @@
mUseConcurrent = sIsProcessAllowedToUseConcurrent;
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
- mThread = Thread.currentThread();
+ mThreadName = Thread.currentThread().getName();
mTid = Process.myTid();
}
@@ -223,10 +220,10 @@
traceMessageCount();
PerfettoTrace.instant(PerfettoTrace.MQ_CATEGORY, "message_queue_send")
- .addFlow(msg.mEventId.get())
+ .setFlow(msg.mEventId.get())
.beginProto()
.beginNested(2004 /* message_queue */)
- .addField(2 /* receiving_thread_name */, mThread.getName())
+ .addField(2 /* receiving_thread_name */, mThreadName)
.addField(3 /* message_code */, msg.what)
.addField(4 /* message_delay_ms */, when - SystemClock.uptimeMillis())
.endNested()
@@ -237,7 +234,7 @@
/** @hide */
private void traceMessageCount() {
PerfettoTrace.counter(PerfettoTrace.MQ_CATEGORY, mMessageCount.get())
- .usingThreadCounterTrack(mTid, mThread.getName())
+ .usingThreadCounterTrack(mTid, mThreadName)
.emit();
}
diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java
index 8cfd324..48e6249 100644
--- a/core/java/android/os/IBinder.java
+++ b/core/java/android/os/IBinder.java
@@ -451,7 +451,7 @@
* @param executor The executor on which to run the callback.
* @param callback The callback used to deliver state change notifications.
*
- * <p>@throws {@link UnsupportedOperationException} if the kernel binder driver does not support
+ * @throws {@link UnsupportedOperationException} if the kernel binder driver does not support
* this feature.
*/
@FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
diff --git a/core/java/android/os/IpcDataCache.java b/core/java/android/os/IpcDataCache.java
index 240bc4f..2e7c3be 100644
--- a/core/java/android/os/IpcDataCache.java
+++ b/core/java/android/os/IpcDataCache.java
@@ -719,9 +719,13 @@
/**
* Enable or disable testing. The protocol requires that the mode toggle: for instance, it is
- * illegal to clear the test mode if the test mode is already off. The purpose is solely to
- * ensure that test clients do not forget to use the test mode properly, even though the
- * current logic does not care.
+ * illegal to clear the test mode if the test mode is already off. Enabling test mode puts
+ * all caches in the process into test mode; all nonces are initialized to UNSET and
+ * subsequent reads and writes are to process memory. This has the effect of disabling all
+ * caches that are not local to the process. Disabling test mode restores caches to normal
+ * operation.
+ * @param mode The desired test mode.
+ * @throws IllegalStateException if the supplied mode is already set.
* @hide
*/
@TestApi
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index 1329b90..3ff6e05 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -205,7 +205,7 @@
.addField(1 /* sending_thread_name */, msg.mSendingThreadName)
.endNested()
.endProto()
- .addTerminatingFlow(msg.mEventId.get())
+ .setTerminatingFlow(msg.mEventId.get())
.emit();
// This must be in a local variabe, in case a UI event sets the logger
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index b22d177..69e84e3 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -23,7 +23,7 @@
import com.android.internal.annotations.VisibleForTesting;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
/**
*
@@ -43,7 +43,7 @@
*
* @hide Only for use within the system server.
*/
- public final AtomicInteger mEventId = new AtomicInteger();
+ public final AtomicLong mEventId = new AtomicLong();
/**
* User-defined message code so that the recipient can identify
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index 727dcba..a6785ba 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -78,7 +78,7 @@
per-file PatternMatcher* = file:/PACKAGE_MANAGER_OWNERS
# PermissionEnforcer
-per-file PermissionEnforcer.java = tweek@google.com, brufino@google.com
+per-file PermissionEnforcer.java = tweek@google.com
# RemoteCallbackList
per-file RemoteCallbackList.java = shayba@google.com
diff --git a/core/java/android/os/PerfettoTrackEventExtra.java b/core/java/android/os/PerfettoTrackEventExtra.java
index f4b5dfe..2848bcb 100644
--- a/core/java/android/os/PerfettoTrackEventExtra.java
+++ b/core/java/android/os/PerfettoTrackEventExtra.java
@@ -172,7 +172,6 @@
private final Pool<FieldDouble> mFieldDoubleCache;
private final Pool<FieldString> mFieldStringCache;
private final Pool<FieldNested> mFieldNestedCache;
- private final Pool<Flow> mFlowCache;
private final Pool<Builder> mBuilderCache;
private Builder() {
@@ -187,7 +186,6 @@
mFieldDoubleCache = mExtra.mFieldDoubleCache;
mFieldStringCache = mExtra.mFieldStringCache;
mFieldNestedCache = mExtra.mFieldNestedCache;
- mFlowCache = mExtra.mFlowCache;
mBuilderCache = mExtra.mBuilderCache;
mCounterInt64 = mExtra.getCounterInt64();
@@ -227,7 +225,6 @@
mFieldStringCache.reset();
mFieldNestedCache.reset();
mBuilderCache.reset();
- mFlowCache.reset();
mExtra.reset();
// Reset after on init in case the thread created builders without calling emit
@@ -325,39 +322,7 @@
/**
* Adds a flow with {@code id}.
*/
- public Builder addFlow(int id) {
- if (!mIsCategoryEnabled) {
- return this;
- }
- if (DEBUG) {
- checkParent();
- }
- Flow flow = mFlowCache.get(sFlowSupplier);
- flow.setProcessFlow(id);
- mExtra.addPerfettoPointer(flow);
- return this;
- }
-
- /**
- * Adds a terminating flow with {@code id}.
- */
- public Builder addTerminatingFlow(int id) {
- if (!mIsCategoryEnabled) {
- return this;
- }
- if (DEBUG) {
- checkParent();
- }
- Flow flow = mFlowCache.get(sFlowSupplier);
- flow.setProcessTerminatingFlow(id);
- mExtra.addPerfettoPointer(flow);
- return this;
- }
-
- /**
- * Adds a flow with {@code id}.
- */
- public Builder setFlow(int id) {
+ public Builder setFlow(long id) {
if (!mIsCategoryEnabled) {
return this;
}
@@ -372,7 +337,7 @@
/**
* Adds a terminating flow with {@code id}.
*/
- public Builder setTerminatingFlow(int id) {
+ public Builder setTerminatingFlow(long id) {
if (!mIsCategoryEnabled) {
return this;
}
@@ -670,7 +635,6 @@
private final Pool<FieldDouble> mFieldDoubleCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
private final Pool<FieldString> mFieldStringCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
private final Pool<FieldNested> mFieldNestedCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
- private final Pool<Flow> mFlowCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
private final Pool<Builder> mBuilderCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
private static final NativeAllocationRegistry sRegistry =
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index 77b6d70..2736b60 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -357,4 +357,9 @@
* This may affect dream eligibility.
*/
public abstract void setDevicePostured(boolean isPostured);
+
+ /**
+ * Notifies PowerManager that settings have changed and that it should refresh its state.
+ */
+ public abstract void updateSettings();
}
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 5188204..561a2c9 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -16,6 +16,7 @@
package android.permission;
+import static android.companion.virtual.VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE;
import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
@@ -2039,12 +2040,49 @@
new PackageNamePermissionQuery(permName, pkgName, persistentDeviceId, userId));
}
+ /**
+ * When checking a device-aware permission on a remote device, if the permission is CAMERA
+ * or RECORD_AUDIO we need to check remote device's corresponding capability. If the remote
+ * device doesn't have capability fall back to checking permission on the default device.
+ *
+ * @hide
+ */
+ public static int resolveDeviceIdForPermissionCheck(@NonNull Context context, int deviceId,
+ @Nullable String permission) {
+ if (deviceId == Context.DEVICE_ID_DEFAULT || !DEVICE_AWARE_PERMISSIONS.contains(
+ permission)) {
+ return Context.DEVICE_ID_DEFAULT;
+ }
+
+ VirtualDeviceManager virtualDeviceManager =
+ context.getSystemService(VirtualDeviceManager.class);
+ if (virtualDeviceManager == null) {
+ Slog.e(LOG_TAG, "VDM is not enabled when device id is not default. deviceId = "
+ + deviceId);
+ } else {
+ VirtualDevice virtualDevice = virtualDeviceManager.getVirtualDevice(deviceId);
+ if (virtualDevice != null) {
+ if ((Objects.equals(permission, Manifest.permission.RECORD_AUDIO)
+ && !virtualDevice.hasCustomAudioInputSupport())
+ || (Objects.equals(permission, Manifest.permission.CAMERA)
+ && !virtualDevice.hasCustomCameraSupport())) {
+ deviceId = Context.DEVICE_ID_DEFAULT;
+ }
+ } else {
+ Slog.e(LOG_TAG,
+ "virtualDevice is not found when device id is not default. deviceId = "
+ + deviceId);
+ }
+ }
+ return deviceId;
+ }
+
@Nullable
private String getPersistentDeviceId(int deviceId) {
String persistentDeviceId = null;
if (deviceId == Context.DEVICE_ID_DEFAULT) {
- persistentDeviceId = VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
+ persistentDeviceId = PERSISTENT_DEVICE_ID_DEFAULT;
} else {
VirtualDeviceManager virtualDeviceManager = mContext.getSystemService(
VirtualDeviceManager.class);
diff --git a/core/java/android/preference/OWNERS b/core/java/android/preference/OWNERS
index b4cb9ec..38a5abd 100644
--- a/core/java/android/preference/OWNERS
+++ b/core/java/android/preference/OWNERS
@@ -1,5 +1,4 @@
lpf@google.com
-pavlis@google.com
clarabayarri@google.com
-per-file SeekBarVolumizer.java = jmtrivi@google.com
\ No newline at end of file
+per-file SeekBarVolumizer.java = jmtrivi@google.com
diff --git a/core/java/android/print/OWNERS b/core/java/android/print/OWNERS
index ce79f5d..15f6406 100644
--- a/core/java/android/print/OWNERS
+++ b/core/java/android/print/OWNERS
@@ -1,5 +1,4 @@
# Bug component: 47273
anothermark@google.com
-kumarashishg@google.com
bmgordon@google.com
diff --git a/core/java/android/printservice/OWNERS b/core/java/android/printservice/OWNERS
index ce79f5d..15f6406 100644
--- a/core/java/android/printservice/OWNERS
+++ b/core/java/android/printservice/OWNERS
@@ -1,5 +1,4 @@
# Bug component: 47273
anothermark@google.com
-kumarashishg@google.com
bmgordon@google.com
diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl
index 3ca9d93..fdacd60 100644
--- a/core/java/android/service/dreams/IDreamManager.aidl
+++ b/core/java/android/service/dreams/IDreamManager.aidl
@@ -59,4 +59,6 @@
float screenBrightnessFloat, int screenBrightnessInt,
boolean useNormalBrightnessForDoze);
oneway void finishSelfOneway(in IBinder token, boolean immediate);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)")
+ void setScreensaverEnabled(boolean enabled);
}
diff --git a/core/java/android/speech/OWNERS b/core/java/android/speech/OWNERS
index 32f4822..f228ba46 100644
--- a/core/java/android/speech/OWNERS
+++ b/core/java/android/speech/OWNERS
@@ -1,3 +1,2 @@
-volnov@google.com
eugeniom@google.com
schfan@google.com
diff --git a/core/java/android/text/OWNERS b/core/java/android/text/OWNERS
index 0935ffd9..b493ef7 100644
--- a/core/java/android/text/OWNERS
+++ b/core/java/android/text/OWNERS
@@ -4,7 +4,6 @@
halilibo@google.com
haoyuchang@google.com
justinghan@google.com
-klippenstein@google.com
nona@google.com
seanmcq@google.com
siyamed@google.com
diff --git a/core/java/android/util/apk/OWNERS b/core/java/android/util/apk/OWNERS
index 0f4e869..f267f9a 100644
--- a/core/java/android/util/apk/OWNERS
+++ b/core/java/android/util/apk/OWNERS
@@ -1,3 +1,2 @@
include /core/java/android/content/pm/OWNERS
-cbrubaker@google.com
mpgroover@google.com
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index b98f4db..4f6c730 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -2247,6 +2247,27 @@
t.reparent(sc, mBlastSurfaceControl).show(sc);
}
+ /**
+ * Populates a {@link ViewStructure} for content capture.
+ *
+ * <p>If {@link #setSecure(boolean)} has been enabled, will add a property to the
+ * {@link android.app.assist.AssistStructure.ViewNode} to indicate that content will not
+ * be available for this part of the screen.
+ *
+ * @hide
+ */
+ @Override
+ protected void onProvideStructure(@NonNull ViewStructure structure,
+ @ViewStructureType int viewFor, int flags) {
+ super.onProvideStructure(structure, viewFor, flags);
+ if (android.app.contextualsearch.flags.Flags.reportSecureSurfacesInAssistStructure()) {
+ if ((mSurfaceFlags & SurfaceControl.SECURE) != 0) {
+ structure.getExtras().putBoolean(
+ ViewStructure.EXTRA_CONTAINS_SECURE_LAYERS, true);
+ }
+ }
+ }
+
/** @hide */
@Override
public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c048d79..f50d77e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -34463,7 +34463,10 @@
*/
@FlaggedApi(android.app.jank.Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
public void reportAppJankStats(@NonNull AppJankStats appJankStats) {
- getRootView().reportAppJankStats(appJankStats);
+ View rootView = getRootView();
+ if (rootView == this) return;
+
+ rootView.reportAppJankStats(appJankStats);
}
/**
@@ -34471,6 +34474,10 @@
* @hide
*/
public @Nullable JankTracker getJankTracker() {
- return getRootView().getJankTracker();
+ View rootView = getRootView();
+ if (rootView == this) {
+ return null;
+ }
+ return rootView.getJankTracker();
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7fd7be8..e157da7 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -153,6 +153,7 @@
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.ResourcesManager;
+import android.app.UiModeManager;
import android.app.WindowConfiguration;
import android.app.compat.CompatChanges;
import android.app.servertransaction.WindowStateTransactionItem;
@@ -196,6 +197,7 @@
import android.hardware.input.InputManagerGlobal;
import android.hardware.input.InputSettings;
import android.media.AudioManager;
+import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -472,8 +474,6 @@
@Nullable
private ContentObserver mForceInvertObserver;
- private static final int INVALID_VALUE = Integer.MIN_VALUE;
- private int mForceInvertEnabled = INVALID_VALUE;
/**
* Callback for notifying about global configuration changes.
*/
@@ -555,6 +555,8 @@
@UiContext
public final Context mContext;
+ private UiModeManager mUiModeManager;
+
@UnsupportedAppUsage
final IWindowSession mWindowSession;
@NonNull Display mDisplay;
@@ -1804,23 +1806,6 @@
}
}
- private boolean isForceInvertEnabled() {
- if (mForceInvertEnabled == INVALID_VALUE) {
- reloadForceInvertEnabled();
- }
- return mForceInvertEnabled == 1;
- }
-
- private void reloadForceInvertEnabled() {
- if (forceInvertColor()) {
- mForceInvertEnabled = Settings.Secure.getIntForUser(
- mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
- /* def= */ 0,
- UserHandle.myUserId());
- }
- }
-
/**
* Register any kind of listeners if setView was success.
*/
@@ -1856,17 +1841,22 @@
mForceInvertObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
- reloadForceInvertEnabled();
updateForceDarkMode();
}
};
- mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(
- Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED
- ),
- false,
- mForceInvertObserver,
- UserHandle.myUserId());
+
+ final Uri[] urisToObserve = {
+ Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED),
+ Settings.Secure.getUriFor(Settings.Secure.UI_NIGHT_MODE)
+ };
+ for (Uri uri : urisToObserve) {
+ mContext.getContentResolver().registerContentObserver(
+ uri,
+ false,
+ mForceInvertObserver,
+ UserHandle.myUserId());
+ }
}
}
}
@@ -2073,21 +2063,25 @@
return getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
}
- /** Returns true if force dark should be enabled according to various settings */
+ /**
+ * Determines the type of force dark to apply, considering force inversion, system night mode,
+ * and app-specific settings (including developer opt-outs).
+ *
+ * @return A {@link ForceDarkType.ForceDarkTypeDef} constant indicating the force dark type.
+ */
@VisibleForTesting
public @ForceDarkType.ForceDarkTypeDef int determineForceDarkType() {
if (forceInvertColor()) {
// Force invert ignores all developer opt-outs.
// We also ignore dark theme, since the app developer can override the user's preference
- // for dark mode in configuration.uiMode. Instead, we assume that the force invert
- // setting will be enabled at the same time dark theme is in the Settings app.
- if (isForceInvertEnabled()) {
+ // for dark mode in configuration.uiMode. Instead, we assume that both force invert and
+ // the system's dark theme are enabled.
+ if (getUiModeManager().getForceInvertState() == UiModeManager.FORCE_INVERT_TYPE_DARK) {
return ForceDarkType.FORCE_INVERT_COLOR_DARK;
}
}
boolean useAutoDark = getNightMode() == Configuration.UI_MODE_NIGHT_YES;
-
if (useAutoDark) {
boolean forceDarkAllowedDefault =
SystemProperties.getBoolean(ThreadedRenderer.DEBUG_FORCE_DARK, false);
@@ -9401,6 +9395,13 @@
return mAudioManager;
}
+ private UiModeManager getUiModeManager() {
+ if (mUiModeManager == null) {
+ mUiModeManager = mContext.getSystemService(UiModeManager.class);
+ }
+ return mUiModeManager;
+ }
+
private Vibrator getSystemVibrator() {
if (mVibrator == null) {
mVibrator = mContext.getSystemService(Vibrator.class);
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index 43a946a..53953a9 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -77,6 +77,19 @@
"android.view.ViewStructure.extra.FIRST_ACTIVE_POSITION";
/**
+ * Key used for confirming whether the view draws graphics containing secure layers.
+ *
+ * <p>Secure layers cannot be read back into main memory and will show up as blank regions
+ * in assist screenshots.
+ *
+ * @see android.view.SurfaceControl#SECURE
+ *
+ * @hide
+ */
+ public static final String EXTRA_CONTAINS_SECURE_LAYERS =
+ "android.view.ViewStructure.extra.CONTAINS_SECURE_LAYERS";
+
+ /**
* Key used for writing the type of the view that generated the virtual structure of its
* children.
*
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 196ae5e..83dc79b 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -631,6 +631,12 @@
int TRANSIT_FLAG_AOD_APPEARING = (1 << 15); // 0x8000
/**
+ * Transition flag: Indicates that the task shouldn't move to front when launching the activity.
+ * @hide
+ */
+ int TRANSIT_FLAG_AVOID_MOVE_TO_FRONT = (1 << 16); // 0x10000
+
+ /**
* @hide
*/
@IntDef(flag = true, prefix = { "TRANSIT_FLAG_" }, value = {
@@ -650,6 +656,7 @@
TRANSIT_FLAG_KEYGUARD_UNOCCLUDING,
TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH,
TRANSIT_FLAG_AOD_APPEARING,
+ TRANSIT_FLAG_AVOID_MOVE_TO_FRONT,
})
@Retention(RetentionPolicy.SOURCE)
@interface TransitionFlags {}
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index c81c2bb..a4ea64e 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -203,3 +203,14 @@
description: "Adding animating insets types and report IME visibility at the beginning of hiding"
bug: "393049691"
}
+
+flag {
+ name: "lower_ime_oom_importance"
+ namespace: "input_method"
+ description: "Lower keyboard app process oom importance to PERCEPTIBLE_APP_ADJ + 1."
+ bug: "372511805"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/view/textclassifier/intent/OWNERS b/core/java/android/view/textclassifier/intent/OWNERS
index dc18514..4a5dfd8 100644
--- a/core/java/android/view/textclassifier/intent/OWNERS
+++ b/core/java/android/view/textclassifier/intent/OWNERS
@@ -2,5 +2,4 @@
toki@google.com
svetoslavganov@android.com
-svetoslavganov@google.com
joannechung@google.com
diff --git a/core/java/android/webkit/TEST_MAPPING b/core/java/android/webkit/TEST_MAPPING
index 3858059..c9b5476 100644
--- a/core/java/android/webkit/TEST_MAPPING
+++ b/core/java/android/webkit/TEST_MAPPING
@@ -17,15 +17,6 @@
"exclude-annotation": "android.test.FlakyTest"
}
]
- },
- {
- "name": "GtsWebViewHostTestCases",
- "keywords": ["internal"],
- "options": [
- {
- "exclude-annotation": "android.test.FlakyTest"
- }
- ]
}
]
}
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 14b208a..ab7a4f2 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -1458,13 +1458,14 @@
public abstract void setNeedInitialFocus(boolean flag);
/**
- * Sets the priority of the Render thread. Unlike the other settings, this
+ * Sets the CPU scheduling priority of the Render thread. Unlike the other settings, this
* one only needs to be called once per process. The default value is
* {@link RenderPriority#NORMAL}.
*
* @param priority the priority
- * @deprecated It is not recommended to adjust thread priorities, and this will
- * not be supported in future versions.
+ * @deprecated This is no longer supported. See {@link WebView#setRendererPriorityPolicy} if you
+ * instead want to control how freely the system should kill the renderer process
+ * under low memory conditions.
*/
@Deprecated
public abstract void setRenderPriority(RenderPriority priority);
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index b666399..5e22c28 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2287,26 +2287,53 @@
public @interface RendererPriority {}
/**
- * The renderer associated with this WebView is bound with
- * {@link Context#BIND_WAIVE_PRIORITY}. At this priority level
- * {@link WebView} renderers will be strong targets for out of memory
- * killing.
+ * This is the lowest binding priority for the WebView renderer process. This is equivalent to
+ * {@link Context#BIND_WAIVE_PRIORITY}. At this priority level {@link WebView} renderers will be
+ * frequent targets for being killed when the system is running low on memory.
*
- * Use with {@link #setRendererPriorityPolicy}.
+ * <p>If using this priority, we recommend handling the {@link
+ * WebViewClient#onRenderProcessGone} callback to recover from low memory kills as well as other
+ * types of renderer crashes.
+ *
+ * @see #setRendererPriorityPolicy
+ * @see #getRendererPriorityPolicy
+ * @see RenderProcessGoneDetail#rendererPriorityAtExit
*/
public static final int RENDERER_PRIORITY_WAIVED = 0;
+
/**
- * The renderer associated with this WebView is bound with
- * the default priority for services.
+ * This is the medium binding priority for the WebView renderer process. This is equivalent to
+ * the standard priority used by the system for calls to {@link
+ * Context#bindService(Intent,android.content.ServiceConnection,int)} when no flags are
+ * provided. At this priority level {@link WebView} renderers will be slightly more likely
+ * targets for being killed when the system is running low on memory.
*
- * Use with {@link #setRendererPriorityPolicy}.
+ * <p>If using this priority, we recommend handling the {@link
+ * WebViewClient#onRenderProcessGone} callback to recover from low memory kills as well as other
+ * types of renderer crashes.
+ *
+ * @see #setRendererPriorityPolicy
+ * @see #getRendererPriorityPolicy
+ * @see RenderProcessGoneDetail#rendererPriorityAtExit
*/
public static final int RENDERER_PRIORITY_BOUND = 1;
+
/**
- * The renderer associated with this WebView is bound with
- * {@link Context#BIND_IMPORTANT}.
+ * This is the highest binding priority for the WebView renderer process, and the default value
+ * WebView uses to bind to the renderer process. This is equivalent to {@link
+ * Context#BIND_IMPORTANT}. At this priority level {@link WebView} renderers are less likely to
+ * be killed when the system is running low on memory and will have the same priority as your
+ * app's main process.
*
- * Use with {@link #setRendererPriorityPolicy}.
+ * <p>It's still possible for the renderer process to be killed when the system is running low
+ * on memory, however specifying this priority makes this situation less likely. It's also still
+ * possible for the renderer process to crash for other reasons. You may optionally still choose
+ * to handle {@link WebViewClient#onRenderProcessGone} in order to recover from a killed or
+ * crashed renderer process.
+ *
+ * @see #setRendererPriorityPolicy
+ * @see #getRendererPriorityPolicy
+ * @see RenderProcessGoneDetail#rendererPriorityAtExit
*/
public static final int RENDERER_PRIORITY_IMPORTANT = 2;
@@ -2316,7 +2343,7 @@
* process renderer should be considered to be a target for OOM
* killing.
*
- * Because a renderer can be associated with more than one
+ * <p>Because a renderer can be associated with more than one
* WebView, the final priority it is computed as the maximum of
* any attached WebViews. When a WebView is destroyed it will
* cease to be considerered when calculating the renderer
@@ -2324,7 +2351,7 @@
* the priority of the renderer will be reduced to
* {@link #RENDERER_PRIORITY_WAIVED}.
*
- * The default policy is to set the priority to
+ * <p>The default policy is to set the priority to
* {@link #RENDERER_PRIORITY_IMPORTANT} regardless of visibility,
* and this should not be changed unless the caller also handles
* renderer crashes with
@@ -2338,6 +2365,8 @@
* when this WebView is not visible, it will be treated as
* if it had requested a priority of
* {@link #RENDERER_PRIORITY_WAIVED}.
+ * @see #getRendererPriorityPolicy
+ * @see RenderProcessGoneDetail#rendererPriorityAtExit
*/
public void setRendererPriorityPolicy(
@RendererPriority int rendererRequestedPriority,
@@ -2349,6 +2378,8 @@
* Get the requested renderer priority for this WebView.
*
* @return the requested renderer priority policy.
+ * @see #setRendererPriorityPolicy
+ * @see RenderProcessGoneDetail#rendererPriorityAtExit
*/
@InspectableProperty(hasAttributeId = false, enumMapping = {
@InspectableProperty.EnumEntry(name = "waived", value = RENDERER_PRIORITY_WAIVED),
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index 0fdc776..3cdf290 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -43,6 +43,8 @@
public enum DesktopModeFlags {
// All desktop mode related flags to be overridden by developer option toggle will be added here
// go/keep-sorted start
+ DISABLE_DESKTOP_LAUNCH_PARAMS_OUTSIDE_DESKTOP_BUG_FIX(
+ Flags::disableDesktopLaunchParamsOutsideDesktopBugFix, false),
DISABLE_NON_RESIZABLE_APP_SNAP_RESIZE(Flags::disableNonResizableAppSnapResizing, true),
ENABLE_ACCESSIBLE_CUSTOM_HEADERS(Flags::enableAccessibleCustomHeaders, true),
ENABLE_APP_HEADER_WITH_TASK_DENSITY(Flags::enableAppHeaderWithTaskDensity, true),
@@ -59,6 +61,8 @@
ENABLE_DESKTOP_COMPAT_UI_VISIBILITY_STATUS(Flags::enableCompatUiVisibilityStatus, true),
ENABLE_DESKTOP_INDICATOR_IN_SEPARATE_THREAD_BUGFIX(
Flags::enableDesktopIndicatorInSeparateThreadBugfix, false),
+ ENABLE_DESKTOP_OPENING_DEEPLINK_MINIMIZE_ANIMATION_BUGFIX(
+ Flags::enableDesktopOpeningDeeplinkMinimizeAnimationBugfix, false),
ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX(
Flags::enableDesktopRecentsTransitionsCornersBugfix, false),
ENABLE_DESKTOP_SKIP_COMPAT_UI_EDUCATION_IN_DESKTOP_MODE_BUGFIX(
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index 09eae28..355a87d 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -100,6 +100,17 @@
}
flag {
+ name: "disable_desktop_launch_params_outside_desktop_bug_fix"
+ namespace: "lse_desktop_experience"
+ description: "Prevents DesktopModeLaunchParamsModifier from modifying launch params for non /n"
+ "desktop launches."
+ bug: "396108436"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "enable_desktop_windowing_wallpaper_activity"
namespace: "lse_desktop_experience"
description: "Enables desktop wallpaper activity to show wallpaper in the desktop mode"
@@ -831,4 +842,14 @@
metadata {
purpose: PURPOSE_BUGFIX
}
+}
+
+flag {
+ name: "enable_desktop_opening_deeplink_minimize_animation_bugfix"
+ namespace: "lse_desktop_experience"
+ description: "Enabling a minimize animation when a new window is opened via deeplink and the Desktop Windowing open windows limit is reached."
+ bug: "360329773"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
\ No newline at end of file
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index 9f768f0..f2efa20 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -152,4 +152,15 @@
metadata {
purpose: PURPOSE_BUGFIX
}
-}
\ No newline at end of file
+}
+
+flag {
+ namespace: "windowing_sdk"
+ name: "cleanup_dispatch_pending_transactions_remote_exception"
+ description: "Refactor to cleanup for RemoteException from dispatchPendingTransactions"
+ bug: "323801078"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/com/android/internal/app/MediaRouteControllerContentManager.java b/core/java/com/android/internal/app/MediaRouteControllerContentManager.java
index 3a8b94f..11093f1 100644
--- a/core/java/com/android/internal/app/MediaRouteControllerContentManager.java
+++ b/core/java/com/android/internal/app/MediaRouteControllerContentManager.java
@@ -206,6 +206,13 @@
mDelegate.dismissView();
}
+ /**
+ * Request the media route to update volume.
+ */
+ public void requestUpdateRouteVolume(int direction) {
+ mRoute.requestUpdateVolume(direction);
+ }
+
private boolean isVolumeControlAvailable() {
return mRoute.getVolumeHandling() == MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE;
}
diff --git a/core/java/com/android/internal/app/MediaRouteControllerDialog.java b/core/java/com/android/internal/app/MediaRouteControllerDialog.java
index 5899963..73f9515 100644
--- a/core/java/com/android/internal/app/MediaRouteControllerDialog.java
+++ b/core/java/com/android/internal/app/MediaRouteControllerDialog.java
@@ -42,19 +42,11 @@
*/
public class MediaRouteControllerDialog extends AlertDialog implements
MediaRouteControllerContentManager.Delegate {
- // TODO(b/360050020): Eventually these 2 variables should be in the content manager instead of
- // here. So these should be removed when the migration is completed.
- private final MediaRouter mRouter;
- private final MediaRouter.RouteInfo mRoute;
-
private final MediaRouteControllerContentManager mContentManager;
public MediaRouteControllerDialog(Context context, int theme) {
super(context, theme);
-
mContentManager = new MediaRouteControllerContentManager(context, this);
- mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
- mRoute = mRouter.getSelectedRoute();
}
@Override
@@ -91,7 +83,8 @@
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
|| keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
- mRoute.requestUpdateVolume(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ? -1 : 1);
+ mContentManager.requestUpdateRouteVolume(
+ keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ? -1 : 1);
return true;
}
return super.onKeyDown(keyCode, event);
diff --git a/core/java/com/android/internal/infra/OWNERS b/core/java/com/android/internal/infra/OWNERS
index 4550358..e69de29 100644
--- a/core/java/com/android/internal/infra/OWNERS
+++ b/core/java/com/android/internal/infra/OWNERS
@@ -1,6 +0,0 @@
-per-file AndroidFuture.java = eugenesusla@google.com
-per-file RemoteStream.java = eugenesusla@google.com
-per-file PerUser.java = eugenesusla@google.com
-per-file ServiceConnector.java = eugenesusla@google.com
-per-file AndroidFuture.aidl = eugenesusla@google.com
-per-file IAndroidFuture.aidl = eugenesusla@google.com
\ No newline at end of file
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index 056a0e8..81ca231 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -789,7 +789,7 @@
*/
public boolean readFragmentToParcel(Parcel out, BatteryHistoryFragment fragment) {
byte[] data = mStore.readFragment(fragment);
- if (data == null) {
+ if (data == null || data.length == 0) {
return false;
}
out.unmarshall(data, 0, data.length);
@@ -934,6 +934,10 @@
continue;
}
+ if (data.length == 0) {
+ continue;
+ }
+
out.writeBoolean(true);
if (useBlobs) {
out.writeBlob(data, 0, data.length);
@@ -976,9 +980,11 @@
return false;
}
- parcel.unmarshall(data, 0, data.length);
- parcel.setDataPosition(0);
- readHistoryBuffer(parcel);
+ if (data.length > 0) {
+ parcel.unmarshall(data, 0, data.length);
+ parcel.setDataPosition(0);
+ readHistoryBuffer(parcel);
+ }
} catch (Exception e) {
Slog.e(TAG, "Error reading battery history", e);
reset();
diff --git a/core/java/com/android/internal/util/OWNERS b/core/java/com/android/internal/util/OWNERS
index 9be8ea7..d174fe3 100644
--- a/core/java/com/android/internal/util/OWNERS
+++ b/core/java/com/android/internal/util/OWNERS
@@ -1,8 +1,8 @@
-per-file AsyncChannel* = lorenzo@google.com, satk@google.com, etancohen@google.com
+per-file AsyncChannel* = lorenzo@google.com, satk@google.com
per-file MessageUtils*, Protocol*, RingBuffer*, TokenBucket* = jchalard@google.com, lorenzo@google.com, satk@google.com
per-file *Notification* = file:/services/core/java/com/android/server/notification/OWNERS
per-file *ContrastColor* = file:/services/core/java/com/android/server/notification/OWNERS
-per-file Protocol* = etancohen@google.com, lorenzo@google.com
+per-file Protocol* =lorenzo@google.com
per-file State* = jchalard@google.com, lorenzo@google.com, satk@google.com
per-file *Dump* = file:/core/java/com/android/internal/util/dump/OWNERS
per-file *Screenshot* = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
diff --git a/core/java/com/android/internal/util/function/pooled/OWNERS b/core/java/com/android/internal/util/function/pooled/OWNERS
index da723b3..e69de29 100644
--- a/core/java/com/android/internal/util/function/pooled/OWNERS
+++ b/core/java/com/android/internal/util/function/pooled/OWNERS
@@ -1 +0,0 @@
-eugenesusla@google.com
\ No newline at end of file
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 453a075..2cca3db 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -86,6 +86,7 @@
public static final Interpolator FAST_OUT_LINEAR_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
public static final Interpolator OVERSHOOT = new PathInterpolator(0.4f, 0f, 0.2f, 1.4f);
+ private static final int MAX_SUMMARIZATION_LINES = 3;
public static final int IMPORTANCE_ANIM_GROW_DURATION = 250;
public static final int IMPORTANCE_ANIM_SHRINK_DURATION = 200;
public static final int IMPORTANCE_ANIM_SHRINK_DELAY = 25;
@@ -401,7 +402,7 @@
public void setIsCollapsed(boolean isCollapsed) {
mIsCollapsed = isCollapsed;
mMessagingLinearLayout.setMaxDisplayedLines(isCollapsed
- ? TextUtils.isEmpty(mSummarizedContent) ? 1 : 2
+ ? TextUtils.isEmpty(mSummarizedContent) ? 1 : MAX_SUMMARIZATION_LINES
: Integer.MAX_VALUE);
updateExpandButton();
updateContentEndPaddings();
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index f2099bc..9fe2de8 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -63,6 +63,7 @@
public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f);
public static final Interpolator FAST_OUT_LINEAR_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+ private static final int MAX_SUMMARIZATION_LINES = 3;
public static final OnLayoutChangeListener MESSAGING_PROPERTY_ANIMATOR
= new MessagingPropertyAnimator();
private final PeopleHelper mPeopleHelper = new PeopleHelper();
@@ -223,7 +224,7 @@
List<MessagingMessage> newMessagingMessages;
mSummarizedContent = extras.getCharSequence(Notification.EXTRA_SUMMARIZED_CONTENT);
if (!TextUtils.isEmpty(mSummarizedContent) && mIsCollapsed) {
- mMessagingLinearLayout.setMaxDisplayedLines(2);
+ mMessagingLinearLayout.setMaxDisplayedLines(MAX_SUMMARIZATION_LINES);
Notification.MessagingStyle.Message summary =
new Notification.MessagingStyle.Message(mSummarizedContent, 0, "");
newMessagingMessages = createMessages(List.of(summary), false, usePrecomputedText);
diff --git a/core/java/com/android/internal/widget/NotificationRowIconView.java b/core/java/com/android/internal/widget/NotificationRowIconView.java
index c96e979..1b29b7f 100644
--- a/core/java/com/android/internal/widget/NotificationRowIconView.java
+++ b/core/java/com/android/internal/widget/NotificationRowIconView.java
@@ -225,6 +225,6 @@
boolean shouldShowAppIcon();
/** Get the app icon for this notification. */
- Drawable getAppIcon();
+ @Nullable Drawable getAppIcon();
}
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 92a841f..748c5b4 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -21,7 +21,6 @@
config_namespace: "ANDROID",
bool_variables: [
"release_binder_death_recipient_weak_from_jni",
- "release_package_libandroid_runtime_punch_holes",
],
properties: [
"cflags",
@@ -66,9 +65,6 @@
release_binder_death_recipient_weak_from_jni: {
cflags: ["-DBINDER_DEATH_RECIPIENT_WEAK_FROM_JNI"],
},
- release_package_libandroid_runtime_punch_holes: {
- cflags: ["-DENABLE_PUNCH_HOLES"],
- },
},
cpp_std: "gnu++20",
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index 06fd80e..14132e6 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -236,7 +236,6 @@
return INSTALL_FAILED_CONTAINER_ERROR;
}
-#ifdef ENABLE_PUNCH_HOLES
// punch extracted elf files as well. This will fail where compression is on (like f2fs) but it
// will be useful for ext4 based systems
struct statfs64 fsInfo;
@@ -253,7 +252,6 @@
zipFile->getZipFileName());
}
}
-#endif // ENABLE_PUNCH_HOLES
ALOGV("Successfully moved %s to %s\n", localTmpFileName, localFileName);
@@ -332,7 +330,6 @@
return INSTALL_FAILED_INVALID_APK;
}
-#ifdef ENABLE_PUNCH_HOLES
// if library is uncompressed, punch hole in it in place
if (!punchHolesInElf64(zipFile->getZipFileName(), offset)) {
ALOGW("Failed to punch uncompressed elf file :%s inside apk : %s at offset: "
@@ -345,7 +342,6 @@
if (!punchHolesInZip(zipFile->getZipFileName(), offset, extraFieldLength)) {
ALOGW("Failed to punch apk : %s at extra field", zipFile->getZipFileName());
}
-#endif // ENABLE_PUNCH_HOLES
return INSTALL_SUCCEEDED;
}
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
index aa8f841..c804024 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -5,7 +5,6 @@
singhtejinder@google.com
yanmin@google.com
yaochen@google.com
-zhouwenjie@google.com
# Frameworks
ogunwale@google.com
diff --git a/core/res/OWNERS b/core/res/OWNERS
index faed4d8..a208f7f 100644
--- a/core/res/OWNERS
+++ b/core/res/OWNERS
@@ -9,7 +9,6 @@
ilyamaty@google.com
jbolinger@google.com
jsharkey@android.com
-jsharkey@google.com
juliacr@google.com
kchyn@google.com
michaelwr@google.com
diff --git a/core/res/res/values/locale_config.xml b/core/res/res/values/locale_config.xml
index 77d2e87..0efa1bc 100644
--- a/core/res/res/values/locale_config.xml
+++ b/core/res/res/values/locale_config.xml
@@ -163,15 +163,18 @@
<item>en-CM</item> <!-- English (Cameroon) -->
<item>en-CX</item> <!-- English (Christmas Island) -->
<item>en-CY</item> <!-- English (Cyprus) -->
+ <item>en-CZ</item> <!-- English (Czechia) -->
<item>en-DE</item> <!-- English (Germany) -->
<item>en-DG</item> <!-- English (Diego Garcia) -->
<item>en-DK</item> <!-- English (Denmark) -->
<item>en-DM</item> <!-- English (Dominica) -->
<item>en-ER</item> <!-- English (Eritrea) -->
+ <item>en-ES</item> <!-- English (Spain) -->
<item>en-FI</item> <!-- English (Finland) -->
<item>en-FJ</item> <!-- English (Fiji) -->
<item>en-FK</item> <!-- English (Falkland Islands (Islas Malvinas)) -->
<item>en-FM</item> <!-- English (Micronesia) -->
+ <item>en-FR</item> <!-- English (France) -->
<item>en-GB</item> <!-- English (United Kingdom) -->
<item>en-GD</item> <!-- English (Grenada) -->
<item>en-GG</item> <!-- English (Guernsey) -->
@@ -181,12 +184,14 @@
<item>en-GU</item> <!-- English (Guam) -->
<item>en-GY</item> <!-- English (Guyana) -->
<item>en-HK</item> <!-- English (Hong Kong) -->
+ <item>en-HU</item> <!-- English (Hungary) -->
<item>en-ID</item> <!-- English (Indonesia) -->
<item>en-IE</item> <!-- English (Ireland) -->
<item>en-IL</item> <!-- English (Israel) -->
<item>en-IM</item> <!-- English (Isle of Man) -->
<item>en-IN</item> <!-- English (India) -->
<item>en-IO</item> <!-- English (British Indian Ocean Territory) -->
+ <item>en-IT</item> <!-- English (Italy) -->
<item>en-JE</item> <!-- English (Jersey) -->
<item>en-JM</item> <!-- English (Jamaica) -->
<item>en-KE</item> <!-- English (Kenya) -->
@@ -210,15 +215,19 @@
<item>en-NF</item> <!-- English (Norfolk Island) -->
<item>en-NG</item> <!-- English (Nigeria) -->
<item>en-NL</item> <!-- English (Netherlands) -->
+ <item>en-NO</item> <!-- English (Norway) -->
<item>en-NR</item> <!-- English (Nauru) -->
<item>en-NU</item> <!-- English (Niue) -->
<item>en-NZ</item> <!-- English (New Zealand) -->
<item>en-PG</item> <!-- English (Papua New Guinea) -->
<item>en-PH</item> <!-- English (Philippines) -->
<item>en-PK</item> <!-- English (Pakistan) -->
+ <item>en-PL</item> <!-- English (Poland) -->
<item>en-PN</item> <!-- English (Pitcairn Islands) -->
<item>en-PR</item> <!-- English (Puerto Rico) -->
+ <item>en-PT</item> <!-- English (Portugal) -->
<item>en-PW</item> <!-- English (Palau) -->
+ <item>en-RO</item> <!-- English (Romania) -->
<item>en-RW</item> <!-- English (Rwanda) -->
<item>en-SB</item> <!-- English (Solomon Islands) -->
<item>en-SC</item> <!-- English (Seychelles) -->
@@ -227,6 +236,7 @@
<item>en-SG</item> <!-- English (Singapore) -->
<item>en-SH</item> <!-- English (St. Helena) -->
<item>en-SI</item> <!-- English (Slovenia) -->
+ <item>en-SK</item> <!-- English (Slovakia) -->
<item>en-SL</item> <!-- English (Sierra Leone) -->
<item>en-SS</item> <!-- English (South Sudan) -->
<item>en-SX</item> <!-- English (Sint Maarten) -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 6a83bae..cb3dfc7 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5063,8 +5063,6 @@
<!-- Notification action button. Click it will open the bluetooth device details page for this hearing device. It will be shown when making a phone call with the hearing device. [CHAR LIMIT=none] -->
<string name="hearing_device_notification_settings_button">Settings</string>
- <!-- Text spoken when the current user is switched if accessibility is enabled. [CHAR LIMIT=none] -->
- <string name="user_switched">Current user <xliff:g id="name" example="Bob">%1$s</xliff:g>.</string>
<!-- Message shown when switching to a user [CHAR LIMIT=none] -->
<string name="user_switching_message">Switching to <xliff:g id="name" example="Bob">%1$s</xliff:g>\u2026</string>
<!-- Message when logging out a user on a split user system -->
diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
index ac78e87..f75a72d 100644
--- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
+++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
@@ -414,16 +414,23 @@
@Test
@DisabledOnRavenwood(reason = "SystemProperties doesn't have permission check")
public void testPermissionFailure() {
- // Create a cache that will write a system nonce.
- TestCache sysCache = new TestCache(MODULE_SYSTEM, "mode1");
try {
- // Invalidate the cache, which writes the system property. There must be a permission
- // failure.
- sysCache.invalidateCache();
- fail("expected permission failure");
- } catch (RuntimeException e) {
- // The expected exception is a bare RuntimeException. The test does not attempt to
- // validate the text of the exception message.
+ // Disable the test mode for this test, but ensure that it will be enabled when the
+ // test exits.
+ PropertyInvalidatedCache.setTestMode(false);
+ // Create a cache that will write a system nonce.
+ TestCache sysCache = new TestCache(MODULE_SYSTEM, "mode1");
+ try {
+ // Invalidate the cache, which writes the system property. There must be a
+ // permission failure.
+ sysCache.invalidateCache();
+ fail("expected permission failure");
+ } catch (RuntimeException e) {
+ // The expected exception is a bare RuntimeException. The test does not attempt
+ // to validate the text of the exception message.
+ }
+ } finally {
+ PropertyInvalidatedCache.setTestMode(true);
}
}
diff --git a/core/tests/coretests/src/android/content/pm/OWNERS b/core/tests/coretests/src/android/content/pm/OWNERS
index 8673365..c4c40dc 100644
--- a/core/tests/coretests/src/android/content/pm/OWNERS
+++ b/core/tests/coretests/src/android/content/pm/OWNERS
@@ -1,5 +1,4 @@
include /core/java/android/content/pm/OWNERS
per-file AppSearchPersonTest.java = file:/core/java/android/content/pm/SHORTCUT_OWNERS
-per-file SigningDetailsTest.java = cbrubaker@google.com
per-file SigningDetailsTest.java = mpgroover@google.com
diff --git a/core/tests/coretests/src/android/os/IpcDataCacheTest.java b/core/tests/coretests/src/android/os/IpcDataCacheTest.java
index 74b32a1..791ec5d 100644
--- a/core/tests/coretests/src/android/os/IpcDataCacheTest.java
+++ b/core/tests/coretests/src/android/os/IpcDataCacheTest.java
@@ -452,22 +452,28 @@
assertTrue(ec.isDisabled());
}
-
// Verify that invalidating the cache from an app process would fail due to lack of permissions.
@Test
@android.platform.test.annotations.DisabledOnRavenwood(
reason = "SystemProperties doesn't have permission check")
public void testPermissionFailure() {
- // Create a cache that will write a system nonce.
- TestCache sysCache = new TestCache(IpcDataCache.MODULE_SYSTEM, "mode1");
try {
- // Invalidate the cache, which writes the system property. There must be a permission
- // failure.
- sysCache.invalidateCache();
- fail("expected permission failure");
- } catch (RuntimeException e) {
- // The expected exception is a bare RuntimeException. The test does not attempt to
- // validate the text of the exception message.
+ // Disable test mode for this test. Guarantee that the mode is enabled before the
+ // test exits.
+ IpcDataCache.setTestMode(false);
+ // Create a cache that will write a system nonce.
+ TestCache sysCache = new TestCache(IpcDataCache.MODULE_SYSTEM, "mode1");
+ try {
+ // Invalidate the cache, which writes the system property. There must be a
+ // permission failure.
+ sysCache.invalidateCache();
+ fail("expected permission failure");
+ } catch (RuntimeException e) {
+ // The expected exception is a bare RuntimeException. The test does not attempt
+ // to validate the text of the exception message.
+ }
+ } finally {
+ IpcDataCache.setTestMode(true);
}
}
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index f5d1e7a..39f3d33 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.app.UiModeManager.MODE_NIGHT_NO;
+import static android.app.UiModeManager.MODE_NIGHT_YES;
import static android.util.SequenceUtils.getInitSeq;
import static android.view.HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING;
import static android.view.InputDevice.SOURCE_ROTARY_ENCODER;
@@ -67,8 +69,10 @@
import android.annotation.NonNull;
import android.app.Instrumentation;
import android.app.UiModeManager;
+import android.app.UiModeManager.ForceInvertType;
import android.content.Context;
import android.graphics.ForceDarkType;
+import android.graphics.ForceDarkType.ForceDarkTypeDef;
import android.graphics.Rect;
import android.hardware.display.DisplayManagerGlobal;
import android.os.Binder;
@@ -93,9 +97,12 @@
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.compatibility.common.util.ShellIdentityUtils;
+import com.android.compatibility.common.util.TestUtils;
import com.android.cts.input.BlockingQueueEventVerifier;
import com.android.window.flags.Flags;
+import com.google.common.truth.Expect;
+
import org.hamcrest.Matcher;
import org.junit.After;
import org.junit.AfterClass;
@@ -124,6 +131,8 @@
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ @Rule
+ public final Expect mExpect = Expect.create();
private ViewRootImpl mViewRootImpl;
private View mView;
@@ -1507,49 +1516,34 @@
}
@Test
- public void forceInvertOffDarkThemeOff_forceDarkModeDisabled() {
- mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);
- ShellIdentityUtils.invokeWithShellPermissions(() -> {
- Settings.Secure.putInt(
- sContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
- /* value= */ 0
- );
- var uiModeManager = sContext.getSystemService(UiModeManager.class);
- uiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO);
- });
+ @RequiresFlagsEnabled(FLAG_FORCE_INVERT_COLOR)
+ public void updateConfiguration_returnsExpectedForceDarkMode() {
+ waitForSystemNightModeActivated(true);
- sInstrumentation.runOnMainSync(() ->
- mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())
- );
+ verifyForceDarkType(/* isAppInNightMode= */ true, /* isForceInvertEnabled= */ true,
+ UiModeManager.FORCE_INVERT_TYPE_DARK, ForceDarkType.FORCE_INVERT_COLOR_DARK);
+ verifyForceDarkType(/* isAppInNightMode= */ true, /* isForceInvertEnabled= */ false,
+ UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE);
+ verifyForceDarkType(/* isAppInNightMode= */ false, /* isForceInvertEnabled= */ true,
+ UiModeManager.FORCE_INVERT_TYPE_DARK, ForceDarkType.FORCE_INVERT_COLOR_DARK);
+ verifyForceDarkType(/* isAppInNightMode= */ false, /* isForceInvertEnabled= */ false,
+ UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE);
- assertThat(mViewRootImpl.determineForceDarkType()).isEqualTo(ForceDarkType.NONE);
+ waitForSystemNightModeActivated(false);
+
+ verifyForceDarkType(/* isAppInNightMode= */ true, /* isForceInvertEnabled= */ true,
+ UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE);
+ verifyForceDarkType(/* isAppInNightMode= */ true, /* isForceInvertEnabled= */ false,
+ UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE);
+ verifyForceDarkType(/* isAppInNightMode= */ false, /* isForceInvertEnabled= */ true,
+ UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE);
+ verifyForceDarkType(/* isAppInNightMode= */ false, /* isForceInvertEnabled= */ false,
+ UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE);
}
@Test
- public void forceInvertOnDarkThemeOff_forceDarkModeEnabled() {
- mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);
- ShellIdentityUtils.invokeWithShellPermissions(() -> {
- Settings.Secure.putInt(
- sContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
- /* value= */ 1
- );
- var uiModeManager = sContext.getSystemService(UiModeManager.class);
- uiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO);
- });
-
- sInstrumentation.runOnMainSync(() ->
- mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())
- );
-
- assertThat(mViewRootImpl.determineForceDarkType())
- .isEqualTo(ForceDarkType.FORCE_INVERT_COLOR_DARK);
- }
-
- @Test
+ @EnableFlags(FLAG_FORCE_INVERT_COLOR)
public void forceInvertOffForceDarkOff_forceDarkModeDisabled() {
- mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);
ShellIdentityUtils.invokeWithShellPermissions(() -> {
Settings.Secure.putInt(
sContext.getContentResolver(),
@@ -1562,15 +1556,14 @@
});
sInstrumentation.runOnMainSync(() ->
- mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())
- );
+ mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId()));
assertThat(mViewRootImpl.determineForceDarkType()).isEqualTo(ForceDarkType.NONE);
}
@Test
+ @EnableFlags(FLAG_FORCE_INVERT_COLOR)
public void forceInvertOffForceDarkOn_forceDarkModeEnabled() {
- mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);
ShellIdentityUtils.invokeWithShellPermissions(() -> {
Settings.Secure.putInt(
sContext.getContentResolver(),
@@ -1582,8 +1575,7 @@
});
sInstrumentation.runOnMainSync(() ->
- mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())
- );
+ mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId()));
assertThat(mViewRootImpl.determineForceDarkType()).isEqualTo(ForceDarkType.FORCE_DARK);
}
@@ -1790,4 +1782,39 @@
() -> view.getViewTreeObserver().removeOnDrawListener(listener));
}
}
+
+ private void waitForSystemNightModeActivated(boolean active) {
+ ShellIdentityUtils.invokeWithShellPermissions(() ->
+ sInstrumentation.runOnMainSync(() -> {
+ var uiModeManager = sContext.getSystemService(UiModeManager.class);
+ uiModeManager.setNightModeActivated(active);
+ }));
+ sInstrumentation.waitForIdleSync();
+ }
+
+ private void verifyForceDarkType(boolean isAppInNightMode, boolean isForceInvertEnabled,
+ @ForceInvertType int expectedForceInvertType,
+ @ForceDarkTypeDef int expectedForceDarkType) {
+ var uiModeManager = sContext.getSystemService(UiModeManager.class);
+ ShellIdentityUtils.invokeWithShellPermissions(() -> {
+ uiModeManager.setApplicationNightMode(
+ isAppInNightMode ? MODE_NIGHT_YES : MODE_NIGHT_NO);
+ Settings.Secure.putInt(
+ sContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
+ isForceInvertEnabled ? 1 : 0);
+ });
+
+ sInstrumentation.runOnMainSync(() ->
+ mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId()));
+ try {
+ TestUtils.waitUntil("Waiting for force invert state changed",
+ () -> (uiModeManager.getForceInvertState() == expectedForceInvertType));
+ } catch (Exception e) {
+ Log.e(TAG, "Unexpected error trying to apply force invert state. " + e);
+ e.printStackTrace();
+ }
+
+ mExpect.that(mViewRootImpl.determineForceDarkType()).isEqualTo(expectedForceDarkType);
+ }
}
diff --git a/core/tests/featureflagtests/OWNERS b/core/tests/featureflagtests/OWNERS
index 2ff4f5a..6784f28 100644
--- a/core/tests/featureflagtests/OWNERS
+++ b/core/tests/featureflagtests/OWNERS
@@ -1,2 +1 @@
-sbasi@google.com
-tmfang@google.com
\ No newline at end of file
+tmfang@google.com
diff --git a/data/etc/OWNERS b/data/etc/OWNERS
index 712042f..1251fce 100644
--- a/data/etc/OWNERS
+++ b/data/etc/OWNERS
@@ -1,6 +1,5 @@
include /PACKAGE_MANAGER_OWNERS
-cbrubaker@google.com
hackbod@android.com
hackbod@google.com
jeffv@google.com
diff --git a/drm/java/android/drm/OWNERS b/drm/java/android/drm/OWNERS
index 4387100..b65cce7 100644
--- a/drm/java/android/drm/OWNERS
+++ b/drm/java/android/drm/OWNERS
@@ -1,4 +1,3 @@
# Bug component: 49079
-jtinker@google.com
robertshih@google.com
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index a0ca098..05e23a0 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -2206,26 +2206,21 @@
* @param fontVariationOverride font variation override. You can pass null or empty string for
* clearing font variation override.
*
- * @return true if the provided font variation settings is valid. Otherwise returns false.
- *
+ * @throws IllegalArgumentException If given string is not a valid font variation settings
+ * format
* @see #getFontVariationSettings()
* @see #setFontVariationSettings(String)
* @see #getFontVariationOverride()
* @see FontVariationAxis
*/
@FlaggedApi(FLAG_TYPEFACE_REDESIGN_READONLY)
- public boolean setFontVariationOverride(@Nullable String fontVariationOverride) {
+ public void setFontVariationOverride(@Nullable String fontVariationOverride) {
if (Objects.equals(fontVariationOverride, mFontVariationOverride)) {
- return true;
+ return;
}
- List<FontVariationAxis> axes;
- try {
- axes = FontVariationAxis.fromFontVariationSettingsForList(fontVariationOverride);
- } catch (IllegalArgumentException e) {
- Log.i(TAG, "failed to parse font variation settings.", e);
- return false;
- }
+ List<FontVariationAxis> axes =
+ FontVariationAxis.fromFontVariationSettingsForList(fontVariationOverride);
long builderPtr = nCreateFontVariationBuilder(axes.size());
for (int i = 0; i < axes.size(); ++i) {
FontVariationAxis axis = axes.get(i);
@@ -2234,7 +2229,6 @@
}
nSetFontVariationOverride(mNativePaint, builderPtr);
mFontVariationOverride = fontVariationOverride;
- return true;
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OWNERS
index 6207e5b0..7e55786 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OWNERS
@@ -4,5 +4,4 @@
mattsziklay@google.com
mdehaini@google.com
pbdr@google.com
-tkachenkoi@google.com
vaniadesmonda@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 305fcdd..be22402 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -117,6 +117,7 @@
import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.UpdateSource;
import com.android.wm.shell.shared.bubbles.BubbleBarUpdate;
import com.android.wm.shell.shared.bubbles.BubbleDropTargetBoundsProvider;
import com.android.wm.shell.shared.bubbles.DeviceConfig;
@@ -795,7 +796,7 @@
* Update bubble bar location and trigger and update to listeners
*/
public void setBubbleBarLocation(BubbleBarLocation bubbleBarLocation,
- @BubbleBarLocation.UpdateSource int source) {
+ @UpdateSource int source) {
if (isShowingAsBubbleBar()) {
updateExpandedViewForBubbleBarLocation(bubbleBarLocation, source);
BubbleBarUpdate bubbleBarUpdate = new BubbleBarUpdate();
@@ -805,7 +806,7 @@
}
private void updateExpandedViewForBubbleBarLocation(BubbleBarLocation bubbleBarLocation,
- @BubbleBarLocation.UpdateSource int source) {
+ @UpdateSource int source) {
if (isShowingAsBubbleBar()) {
BubbleBarLocation previousLocation = mBubblePositioner.getBubbleBarLocation();
mBubblePositioner.setBubbleBarLocation(bubbleBarLocation);
@@ -818,7 +819,7 @@
private void logBubbleBarLocationIfChanged(BubbleBarLocation location,
BubbleBarLocation previous,
- @BubbleBarLocation.UpdateSource int source) {
+ @UpdateSource int source) {
if (mLayerView == null) {
return;
}
@@ -830,25 +831,25 @@
return;
}
switch (source) {
- case BubbleBarLocation.UpdateSource.DRAG_BAR:
- case BubbleBarLocation.UpdateSource.A11Y_ACTION_BAR:
+ case UpdateSource.DRAG_BAR:
+ case UpdateSource.A11Y_ACTION_BAR:
mLogger.log(onLeft ? BubbleLogger.Event.BUBBLE_BAR_MOVED_LEFT_DRAG_BAR
: BubbleLogger.Event.BUBBLE_BAR_MOVED_RIGHT_DRAG_BAR);
break;
- case BubbleBarLocation.UpdateSource.DRAG_BUBBLE:
- case BubbleBarLocation.UpdateSource.A11Y_ACTION_BUBBLE:
+ case UpdateSource.DRAG_BUBBLE:
+ case UpdateSource.A11Y_ACTION_BUBBLE:
mLogger.log(onLeft ? BubbleLogger.Event.BUBBLE_BAR_MOVED_LEFT_DRAG_BUBBLE
: BubbleLogger.Event.BUBBLE_BAR_MOVED_RIGHT_DRAG_BUBBLE);
break;
- case BubbleBarLocation.UpdateSource.DRAG_EXP_VIEW:
- case BubbleBarLocation.UpdateSource.A11Y_ACTION_EXP_VIEW:
+ case UpdateSource.DRAG_EXP_VIEW:
+ case UpdateSource.A11Y_ACTION_EXP_VIEW:
// TODO(b/349845968): move logging from BubbleBarLayerView to here
break;
- case BubbleBarLocation.UpdateSource.APP_ICON_DRAG:
+ case UpdateSource.APP_ICON_DRAG:
mLogger.log(onLeft ? BubbleLogger.Event.BUBBLE_BAR_MOVED_LEFT_APP_ICON_DROP
: BubbleLogger.Event.BUBBLE_BAR_MOVED_RIGHT_APP_ICON_DROP);
break;
- case BubbleBarLocation.UpdateSource.DRAG_TASK:
+ case UpdateSource.DRAG_TASK:
mLogger.log(onLeft ? BubbleLogger.Event.BUBBLE_BAR_MOVED_LEFT_DRAG_TASK
: BubbleLogger.Event.BUBBLE_BAR_MOVED_RIGHT_DRAG_TASK);
break;
@@ -872,10 +873,7 @@
if (bubbleBarLocation == null) return;
if (isShowingAsBubbleBar() && BubbleAnythingFlagHelper.enableCreateAnyBubble()) {
mBubbleStateListener.onDragItemOverBubbleBarDragZone(bubbleBarLocation);
- ensureBubbleViewsAndWindowCreated();
- if (mLayerView != null) {
- mLayerView.showBubbleBarExtendedViewDropTarget(bubbleBarLocation);
- }
+ showBubbleBarExpandedViewDropTarget(bubbleBarLocation);
}
}
@@ -921,6 +919,13 @@
return result;
}
+ private void showBubbleBarExpandedViewDropTarget(BubbleBarLocation bubbleBarLocation) {
+ ensureBubbleViewsAndWindowCreated();
+ if (mLayerView != null) {
+ mLayerView.showBubbleBarExtendedViewDropTarget(bubbleBarLocation);
+ }
+ }
+
private void hideBubbleBarExpandedViewDropTarget() {
if (mLayerView != null) {
mLayerView.hideBubbleBarExpandedViewDropTarget();
@@ -1541,20 +1546,9 @@
public void expandStackAndSelectBubble(ShortcutInfo info,
@Nullable BubbleBarLocation bubbleBarLocation) {
if (!BubbleAnythingFlagHelper.enableCreateAnyBubble()) return;
- BubbleBarLocation updateLocation = isShowingAsBubbleBar() ? bubbleBarLocation : null;
- if (updateLocation != null) {
- updateExpandedViewForBubbleBarLocation(updateLocation,
- BubbleBarLocation.UpdateSource.APP_ICON_DRAG);
- }
Bubble b = mBubbleData.getOrCreateBubble(info); // Removes from overflow
ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubble - shortcut=%s", info);
- if (b.isInflated()) {
- mBubbleData.setSelectedBubbleAndExpandStack(b, updateLocation);
- } else {
- b.enable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
- inflateAndAdd(b, /* suppressFlyout= */ true, /* showInShade= */ false,
- updateLocation);
- }
+ expandStackAndSelectAppBubble(b, bubbleBarLocation, UpdateSource.APP_ICON_DRAG);
}
/**
@@ -1562,16 +1556,12 @@
*
* @param intent the intent for the bubble.
*/
- public void expandStackAndSelectBubble(Intent intent, UserHandle user) {
+ public void expandStackAndSelectBubble(Intent intent, UserHandle user,
+ @Nullable BubbleBarLocation bubbleBarLocation) {
if (!BubbleAnythingFlagHelper.enableCreateAnyBubble()) return;
Bubble b = mBubbleData.getOrCreateBubble(intent, user); // Removes from overflow
ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubble - intent=%s", intent);
- if (b.isInflated()) {
- mBubbleData.setSelectedBubbleAndExpandStack(b);
- } else {
- b.enable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
- inflateAndAdd(b, /* suppressFlyout= */ true, /* showInShade= */ false);
- }
+ expandStackAndSelectAppBubble(b, bubbleBarLocation, UpdateSource.APP_ICON_DRAG);
}
/**
@@ -1583,14 +1573,19 @@
public void expandStackAndSelectBubble(PendingIntent pendingIntent, UserHandle user,
@Nullable BubbleBarLocation bubbleBarLocation) {
if (!BubbleAnythingFlagHelper.enableCreateAnyBubble()) return;
- BubbleBarLocation updateLocation = isShowingAsBubbleBar() ? bubbleBarLocation : null;
- if (updateLocation != null) {
- updateExpandedViewForBubbleBarLocation(updateLocation,
- BubbleBarLocation.UpdateSource.APP_ICON_DRAG);
- }
- Bubble b = mBubbleData.getOrCreateBubble(pendingIntent, user);
+ Bubble b = mBubbleData.getOrCreateBubble(pendingIntent, user); // Removes from overflow
ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubble - pendingIntent=%s",
pendingIntent);
+ expandStackAndSelectAppBubble(b, bubbleBarLocation, UpdateSource.APP_ICON_DRAG);
+ }
+
+ private void expandStackAndSelectAppBubble(Bubble b,
+ @Nullable BubbleBarLocation bubbleBarLocation, @UpdateSource int source) {
+ if (!BubbleAnythingFlagHelper.enableCreateAnyBubble()) return;
+ BubbleBarLocation updateLocation = isShowingAsBubbleBar() ? bubbleBarLocation : null;
+ if (updateLocation != null) {
+ updateExpandedViewForBubbleBarLocation(updateLocation, source);
+ }
if (b.isInflated()) {
mBubbleData.setSelectedBubbleAndExpandStack(b, updateLocation);
} else {
@@ -1623,7 +1618,7 @@
}
} else {
if (location != null) {
- setBubbleBarLocation(location, BubbleBarLocation.UpdateSource.DRAG_TASK);
+ setBubbleBarLocation(location, UpdateSource.DRAG_TASK);
}
b.enable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
// Lazy init stack view when a bubble is created
@@ -2840,14 +2835,16 @@
}
@Override
- public void showShortcutBubble(ShortcutInfo info) {
+ public void showShortcutBubble(ShortcutInfo info, @Nullable BubbleBarLocation location) {
mMainExecutor.execute(() -> mController
- .expandStackAndSelectBubble(info, /* bubbleBarLocation = */ null));
+ .expandStackAndSelectBubble(info, location));
}
@Override
- public void showAppBubble(Intent intent, UserHandle user) {
- mMainExecutor.execute(() -> mController.expandStackAndSelectBubble(intent, user));
+ public void showAppBubble(Intent intent, UserHandle user,
+ @Nullable BubbleBarLocation location) {
+ mMainExecutor.execute(
+ () -> mController.expandStackAndSelectBubble(intent, user, location));
}
@Override
@@ -2900,7 +2897,7 @@
@Override
public void setBubbleBarLocation(BubbleBarLocation location,
- @BubbleBarLocation.UpdateSource int source) {
+ @UpdateSource int source) {
mMainExecutor.execute(() ->
mController.setBubbleBarLocation(location, source));
}
@@ -2921,6 +2918,17 @@
}
});
}
+
+ @Override
+ public void showDropTarget(boolean show, BubbleBarLocation location) {
+ mMainExecutor.execute(() -> {
+ if (show) {
+ showBubbleBarExpandedViewDropTarget(location);
+ } else {
+ hideBubbleBarExpandedViewDropTarget();
+ }
+ });
+ }
}
private class BubblesImpl implements Bubbles {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
index 0a4d79a..ae1b407 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
@@ -51,9 +51,11 @@
oneway void stopBubbleDrag(in BubbleBarLocation location, in int topOnScreen) = 11;
- oneway void showShortcutBubble(in ShortcutInfo info) = 12;
+ oneway void showShortcutBubble(in ShortcutInfo info, in @nullable BubbleBarLocation location) = 12;
- oneway void showAppBubble(in Intent intent, in UserHandle user) = 13;
+ oneway void showAppBubble(in Intent intent, in UserHandle user, in @nullable BubbleBarLocation location) = 13;
oneway void showExpandedView() = 14;
+
+ oneway void showDropTarget(in boolean show, in @nullable BubbleBarLocation location) = 15;
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt
index aa50772..e3a71a1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt
@@ -18,6 +18,7 @@
import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.window.DesktopExperienceFlags
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UnminimizeReason
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.UNKNOWN
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.transition.FocusTransitionObserver
@@ -188,8 +189,12 @@
pw.println("Error: task id should be an integer")
return false
}
- pw.println("Not implemented.")
- return false
+ controller.moveTaskToFront(
+ /* taskId= */ taskId,
+ /* remoteTransition= */ null,
+ /* unminimizeReason= */ UnminimizeReason.UNKNOWN,
+ )
+ return true
}
private fun runMoveTaskOutOfDesk(args: Array<String>, pw: PrintWriter): Boolean {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
index 6034299..70a648f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
@@ -35,7 +35,7 @@
desktopRepository.removeTask(taskInfo.displayId, taskInfo.taskId)
return
}
- if (isFreeformTask(taskInfo)) {
+ if (isFreeformTask(taskInfo) && !desktopRepository.isActiveTask(taskInfo.taskId)) {
desktopRepository.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
}
}
@@ -44,23 +44,33 @@
logD("onTaskChanging for taskId=%d, displayId=%d", taskInfo.taskId, taskInfo.displayId)
val desktopRepository: DesktopRepository =
desktopUserRepositories.getProfile(taskInfo.userId)
- if (!desktopRepository.isActiveTask(taskInfo.taskId)) return
-
// TODO: b/394281403 - with multiple desks, it's possible to have a non-freeform task
// inside a desk, so this should be decoupled from windowing mode.
// Also, changes in/out of desks are handled by the [DesksTransitionObserver], which has
// more specific information about the desk involved in the transition, which might be
// more accurate than assuming it's always the default/active desk in the display, as this
// method does.
- // Case 1: Freeform task is changed in Desktop Mode.
- if (isFreeformTask(taskInfo)) {
- if (taskInfo.isVisible) {
- desktopRepository.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
+ // Case 1: When the task change is from a task in the desktop repository which is now
+ // fullscreen,
+ // remove the task from the desktop repository since it is no longer a freeform task.
+ if (!isFreeformTask(taskInfo)) {
+ if (desktopRepository.isActiveTask(taskInfo.taskId)) {
+ desktopRepository.removeTask(taskInfo.displayId, taskInfo.taskId)
}
- desktopRepository.updateTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
- } else {
- // Case 2: Freeform task is changed outside Desktop Mode.
- desktopRepository.removeTask(taskInfo.displayId, taskInfo.taskId)
+ } else { // Task change is a freeform task
+ if (!desktopRepository.isActiveTask(taskInfo.taskId)) {
+ // Case 2: When the task change is a freeform visible task, but the task is not
+ // yet active in the desktop repository, adds task to desktop repository.
+ desktopRepository.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
+ } else {
+ // Case 3: When the task change is a freeform task which already exists as an active
+ // task in the desktop repository, updates the task state.
+ desktopRepository.updateTask(
+ taskInfo.displayId,
+ taskInfo.taskId,
+ taskInfo.isVisible,
+ )
+ }
}
}
@@ -82,14 +92,22 @@
logD("onTaskMovingToFront for taskId=%d, displayId=%d", taskInfo.taskId, taskInfo.displayId)
val desktopRepository: DesktopRepository =
desktopUserRepositories.getProfile(taskInfo.userId)
- if (!desktopRepository.isActiveTask(taskInfo.taskId)) return
- if (!isFreeformTask(taskInfo)) {
+ // When the task change is from a task in the desktop repository which is now fullscreen,
+ // remove the task from the desktop repository since it is no longer a freeform task.
+ if (!isFreeformTask(taskInfo) && desktopRepository.isActiveTask(taskInfo.taskId)) {
desktopRepository.removeTask(taskInfo.displayId, taskInfo.taskId)
}
- desktopRepository.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
+ if (isFreeformTask(taskInfo)) {
+ // If the task is already active in the repository, then it only moves the task to the
+ // front.
+ desktopRepository.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
+ }
}
override fun onTaskMovingToBack(taskInfo: RunningTaskInfo) {
+ val desktopRepository: DesktopRepository =
+ desktopUserRepositories.getProfile(taskInfo.userId)
+ if (!desktopRepository.isActiveTask(taskInfo.taskId)) return
logD("onTaskMovingToBack for taskId=%d, displayId=%d", taskInfo.taskId, taskInfo.displayId)
// TODO: b/367268953 - Connect this with DesktopRepository.
}
@@ -101,7 +119,7 @@
if (!desktopRepository.isActiveTask(taskInfo.taskId)) return
// TODO: b/370038902 - Handle Activity#finishAndRemoveTask.
if (
- !DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue() ||
+ !DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue ||
desktopRepository.isClosingTask(taskInfo.taskId)
) {
// A task that's vanishing should be removed:
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 644c5b0..7e63250 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -472,15 +472,14 @@
remoteTransition: RemoteTransition? = null,
callback: IMoveToDesktopCallback? = null,
): Boolean {
- val runningTask = shellTaskOrganizer.getRunningTaskInfo(taskId)
- val backgroundTask = recentTasksController?.findTaskInBackground(taskId)
- if (runningTask == null && backgroundTask == null) {
+ val task =
+ shellTaskOrganizer.getRunningTaskInfo(taskId)
+ ?: recentTasksController?.findTaskInBackground(taskId)
+ if (task == null) {
logW("moveTaskToDefaultDeskAndActivate taskId=%d not found", taskId)
return false
}
- // TODO(342378842): Instead of using default display, support multiple displays
- val displayId = runningTask?.displayId ?: DEFAULT_DISPLAY
- val deskId = getDefaultDeskId(displayId)
+ val deskId = getDefaultDeskId(task.displayId)
return moveTaskToDesk(
taskId = taskId,
deskId = deskId,
@@ -532,14 +531,14 @@
remoteTransition: RemoteTransition? = null,
callback: IMoveToDesktopCallback? = null,
): Boolean {
- if (recentTasksController?.findTaskInBackground(taskId) == null) {
+ val task = recentTasksController?.findTaskInBackground(taskId)
+ if (task == null) {
logW("moveBackgroundTaskToDesktop taskId=%d not found", taskId)
return false
}
logV("moveBackgroundTaskToDesktop with taskId=%d", taskId)
- // TODO(342378842): Instead of using default display, support multiple displays
val taskIdToMinimize =
- bringDesktopAppsToFrontBeforeShowingNewTask(DEFAULT_DISPLAY, wct, taskId)
+ bringDesktopAppsToFrontBeforeShowingNewTask(task.displayId, wct, taskId)
val exitResult =
desktopImmersiveController.exitImmersiveIfApplicable(
wct = wct,
@@ -1122,12 +1121,13 @@
excludeTaskId = launchingTaskId,
reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH,
)
- var deskIdToActivate: Int? = null
- if (
- DesktopExperienceFlags.ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING.isTrue &&
+ var activationRunOnTransitStart: RunOnTransitStart? = null
+ val shouldActivateDesk =
+ (DesktopExperienceFlags.ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING.isTrue ||
+ DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) &&
!isDesktopModeShowing(displayId)
- ) {
- deskIdToActivate =
+ if (shouldActivateDesk) {
+ val deskIdToActivate =
checkNotNull(
launchingTaskId?.let { taskRepository.getDeskIdForTask(it) }
?: getDefaultDeskId(displayId)
@@ -1137,6 +1137,18 @@
// Desk activation must be handled before app launch-related transactions.
activateDeskWct.merge(launchTransaction, /* transfer= */ true)
launchTransaction = activateDeskWct
+ activationRunOnTransitStart = { transition ->
+ desksTransitionObserver.addPendingTransition(
+ DeskTransition.ActivateDesk(
+ token = transition,
+ displayId = displayId,
+ deskId = deskIdToActivate,
+ )
+ )
+ }
+ desktopModeEnterExitTransitionListener?.onEnterDesktopModeTransitionStarted(
+ FREEFORM_ANIMATION_DURATION
+ )
}
val t =
if (remoteTransition == null) {
@@ -1170,24 +1182,7 @@
if (launchingTaskId != null && taskRepository.isMinimizedTask(launchingTaskId)) {
addPendingUnminimizeTransition(t, displayId, launchingTaskId, unminimizeReason)
}
- if (
- DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue &&
- deskIdToActivate != null
- ) {
- if (DesktopExperienceFlags.ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING.isTrue) {
- desksTransitionObserver.addPendingTransition(
- DeskTransition.ActivateDesk(
- token = t,
- displayId = displayId,
- deskId = deskIdToActivate,
- )
- )
- }
-
- desktopModeEnterExitTransitionListener?.onEnterDesktopModeTransitionStarted(
- FREEFORM_ANIMATION_DURATION
- )
- }
+ activationRunOnTransitStart?.invoke(t)
exitImmersiveResult.asExit()?.runOnTransitionStart?.invoke(t)
return t
}
@@ -1321,25 +1316,23 @@
applyFreeformDisplayChange(wct, task, displayId)
}
- val activationRunnable: RunOnTransitStart?
if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
desksOrganizer.moveTaskToDesk(wct, destinationDeskId, task)
- prepareForDeskActivation(displayId, wct)
- desksOrganizer.activateDesk(wct, destinationDeskId)
- activationRunnable = { transition ->
- desksTransitionObserver.addPendingTransition(
- DeskTransition.ActiveDeskWithTask(
- token = transition,
- displayId = displayId,
- deskId = destinationDeskId,
- enterTaskId = task.taskId,
- )
- )
- }
} else {
wct.reparent(task.token, displayAreaInfo.token, /* onTop= */ true)
- activationRunnable = null
}
+ addDeskActivationChanges(destinationDeskId, wct)
+ val activationRunnable: RunOnTransitStart = { transition ->
+ desksTransitionObserver.addPendingTransition(
+ DeskTransition.ActiveDeskWithTask(
+ token = transition,
+ displayId = displayId,
+ deskId = destinationDeskId,
+ enterTaskId = task.taskId,
+ )
+ )
+ }
+
if (Flags.enableDisplayFocusInShellTransitions()) {
// Bring the destination display to top with includingParents=true, so that the
// destination display gains the display focus, which makes the top task in the display
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS
index 44d46ee..7a63ec5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS
@@ -2,7 +2,6 @@
atsjenk@google.com
jorgegil@google.com
madym@google.com
-nmusgrave@google.com
pbdr@google.com
vaniadesmonda@google.com
pragyabajoria@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/OWNERS
index 5aa3c4e..245669b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/OWNERS
@@ -1,3 +1,2 @@
# WM shell sub-module TV pip owner
-galinap@google.com
bronger@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
index 1ce24f7..d16c578 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
@@ -345,7 +345,6 @@
return;
}
- mPipTouchHandler.updateMinMaxSize(mPipBoundsState.getAspectRatio());
mPipMenuController.hideMenu();
if (mPipTransitionState.isInFixedRotation()) {
@@ -366,6 +365,8 @@
mPipBoundsState.setBounds(toBounds);
}
t.setBounds(mPipTransitionState.getPipTaskToken(), mPipBoundsState.getBounds());
+ // Update the size spec in PipBoundsState afterwards.
+ mPipBoundsState.updateMinMaxSize(mPipBoundsState.getAspectRatio());
}
private void setDisplayLayout(DisplayLayout layout) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
index af1e98a..d53365a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
@@ -81,8 +81,6 @@
private final PointF mDownSecondPoint = new PointF();
private final PointF mLastPoint = new PointF();
private final PointF mLastSecondPoint = new PointF();
- private final Point mMaxSize = new Point();
- private final Point mMinSize = new Point();
private final Rect mLastResizeBounds = new Rect();
private final Rect mUserResizeBounds = new Rect();
private final Rect mDownBounds = new Rect();
@@ -95,7 +93,6 @@
private boolean mIsEnabled;
private boolean mEnablePinchResize;
private boolean mEnableDragCornerResize;
- private boolean mIsSysUiStateValid;
private boolean mThresholdCrossed;
private boolean mOngoingPinchToResize = false;
private boolean mWaitingForBoundsChangeTransition = false;
@@ -152,7 +149,6 @@
}
void init() {
- mContext.getDisplay().getRealSize(mMaxSize);
reloadResources();
final Resources res = mContext.getResources();
@@ -163,15 +159,6 @@
reloadResources();
}
- /**
- * Called when SysUI state changed.
- *
- * @param isSysUiStateValid Is SysUI valid or not.
- */
- public void onSystemUiStateChanged(boolean isSysUiStateValid) {
- mIsSysUiStateValid = isSysUiStateValid;
- }
-
private void reloadResources() {
mPipDragToResizeHandler.reloadResources();
mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
@@ -287,13 +274,15 @@
}
}
+ final Point minSize = mPipBoundsState.getMinSize();
+ final Point maxSize = mPipBoundsState.getMaxSize();
if (mOngoingPinchToResize) {
mPipPinchToResizeHandler.onPinchResize(mv, mDownPoint, mDownSecondPoint,
mDownBounds, mLastPoint, mLastSecondPoint, mLastResizeBounds, mTouchSlop,
- mMinSize, mMaxSize);
+ minSize, maxSize);
} else if (mEnableDragCornerResize) {
mPipDragToResizeHandler.onDragCornerResize(mv, mLastResizeBounds, mDownPoint,
- mDownBounds, mMinSize, mMaxSize, mTouchSlop);
+ mDownBounds, minSize, maxSize, mTouchSlop);
}
}
}
@@ -327,7 +316,7 @@
if (mEnablePinchResize && ev.getPointerCount() == 2) {
mPipPinchToResizeHandler.onPinchResize(ev, mDownPoint, mDownSecondPoint,
mDownBounds, mLastPoint, mLastSecondPoint, mLastResizeBounds,
- mTouchSlop, mMinSize, mMaxSize);
+ mTouchSlop, mPipBoundsState.getMinSize(), mPipBoundsState.getMaxSize());
mOngoingPinchToResize = mAllowGesture;
return mAllowGesture;
}
@@ -407,16 +396,19 @@
return;
}
+ final Point minSize = mPipBoundsState.getMinSize();
+ final Point maxSize = mPipBoundsState.getMaxSize();
+
// If user resize is pretty close to max size, just auto resize to max.
- if (mLastResizeBounds.width() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.x
- || mLastResizeBounds.height() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.y) {
- resizeRectAboutCenter(mLastResizeBounds, mMaxSize.x, mMaxSize.y);
+ if (mLastResizeBounds.width() >= PINCH_RESIZE_AUTO_MAX_RATIO * maxSize.x
+ || mLastResizeBounds.height() >= PINCH_RESIZE_AUTO_MAX_RATIO * maxSize.y) {
+ resizeRectAboutCenter(mLastResizeBounds, maxSize.x, maxSize.y);
}
// If user resize is smaller than min size, auto resize to min
- if (mLastResizeBounds.width() < mMinSize.x
- || mLastResizeBounds.height() < mMinSize.y) {
- resizeRectAboutCenter(mLastResizeBounds, mMinSize.x, mMinSize.y);
+ if (mLastResizeBounds.width() < minSize.x
+ || mLastResizeBounds.height() < minSize.y) {
+ resizeRectAboutCenter(mLastResizeBounds, minSize.x, minSize.y);
}
// get the current movement bounds
@@ -472,15 +464,6 @@
mInputMonitor.pilferPointers();
}
-
- void updateMaxSize(int maxX, int maxY) {
- mMaxSize.set(maxX, maxY);
- }
-
- void updateMinSize(int minX, int minY) {
- mMinSize.set(minX, minY);
- }
-
void setOhmOffset(int offset) {
mOhmOffset = offset;
}
@@ -568,8 +551,6 @@
pw.println(innerPrefix + "mEnableDragCornerResize=" + mEnableDragCornerResize);
pw.println(innerPrefix + "mThresholdCrossed=" + mThresholdCrossed);
pw.println(innerPrefix + "mOhmOffset=" + mOhmOffset);
- pw.println(innerPrefix + "mMinSize=" + mMinSize);
- pw.println(innerPrefix + "mMaxSize=" + mMaxSize);
}
class PipResizeInputEventReceiver extends BatchedInputEventReceiver {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
index 72346b3..6fdfeca 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
@@ -230,10 +230,7 @@
pipBoundsState, mTouchState, mPipScheduler, mPipTransitionState, pipUiEventLogger,
menuController, this::getMovementBounds, mPipDisplayLayoutState, pipDesktopState,
mainExecutor, mPipPerfHintController);
- mPipBoundsState.addOnAspectRatioChangedCallback(aspectRatio -> {
- updateMinMaxSize(aspectRatio);
- onAspectRatioChanged();
- });
+ mPipBoundsState.addOnAspectRatioChangedCallback(aspectRatio -> onAspectRatioChanged());
mMoveOnShelVisibilityChanged = () -> {
if (mIsImeShowing && mImeHeight > mShelfHeight) {
@@ -418,15 +415,6 @@
mMainExecutor.executeDelayed(mMoveOnShelVisibilityChanged, PIP_KEEP_CLEAR_AREAS_DELAY);
}
- /**
- * Called when SysUI state changed.
- *
- * @param isSysUiStateValid Is SysUI valid or not.
- */
- public void onSystemUiStateChanged(boolean isSysUiStateValid) {
- mPipResizeGestureHandler.onSystemUiStateChanged(isSysUiStateValid);
- }
-
void adjustBoundsForRotation(Rect outBounds, Rect curBounds, Rect insetBounds) {
final Rect toMovementBounds = new Rect();
mPipBoundsAlgorithm.getMovementBounds(outBounds, insetBounds, toMovementBounds, 0);
@@ -480,8 +468,6 @@
mPipBoundsState.getExpandedBounds(), insetBounds, expandedMovementBounds,
bottomOffset);
- updatePipSizeConstraints(normalBounds, aspectRatio);
-
// The extra offset does not really affect the movement bounds, but are applied based on the
// current state (ime showing, or shelf offset) when we need to actually shift
int extraOffset = Math.max(
@@ -507,35 +493,6 @@
}
/**
- * Update the values for min/max allowed size of picture in picture window based on the aspect
- * ratio.
- * @param aspectRatio aspect ratio to use for the calculation of min/max size
- */
- public void updateMinMaxSize(float aspectRatio) {
- updatePipSizeConstraints(mPipBoundsState.getNormalBounds(),
- aspectRatio);
- }
-
- private void updatePipSizeConstraints(Rect normalBounds,
- float aspectRatio) {
- if (mPipResizeGestureHandler.isUsingPinchToZoom()) {
- updatePinchResizeSizeConstraints(aspectRatio);
- } else {
- mPipResizeGestureHandler.updateMinSize(normalBounds.width(), normalBounds.height());
- mPipResizeGestureHandler.updateMaxSize(mPipBoundsState.getExpandedBounds().width(),
- mPipBoundsState.getExpandedBounds().height());
- }
- }
-
- private void updatePinchResizeSizeConstraints(float aspectRatio) {
- mPipBoundsState.updateMinMaxSize(aspectRatio);
- mPipResizeGestureHandler.updateMinSize(mPipBoundsState.getMinSize().x,
- mPipBoundsState.getMinSize().y);
- mPipResizeGestureHandler.updateMaxSize(mPipBoundsState.getMaxSize().x,
- mPipBoundsState.getMaxSize().y);
- }
-
- /**
* TODO Add appropriate description
*/
public void onRegistrationChanged(boolean isRegistered) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 0438d16..a3a808d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -93,6 +93,7 @@
import android.app.ActivityOptions;
import android.app.IActivityTaskManager;
import android.app.PendingIntent;
+import android.app.PictureInPictureParams;
import android.app.TaskInfo;
import android.content.ActivityNotFoundException;
import android.content.Context;
@@ -2079,8 +2080,8 @@
Math.max(topLeftBounds.top, 0);
bottomRightBounds.right =
Math.min(bottomRightBounds.right, mSplitLayout.getDisplayWidth());
- bottomRightBounds.top =
- Math.min(bottomRightBounds.top, mSplitLayout.getDisplayHeight());
+ bottomRightBounds.bottom =
+ Math.min(bottomRightBounds.bottom, mSplitLayout.getDisplayHeight());
// TODO (b/349828130): Can change to getState() fully after brief soak time.
if (mSplitState.get() != currentSnapPosition) {
@@ -2890,6 +2891,16 @@
prepareEnterSplitScreen(out);
mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(),
TRANSIT_SPLIT_SCREEN_PAIR_OPEN, !mIsDropEntering);
+ } else if (isSplitScreenVisible() && isOpening) {
+ // launching into an existing split stage; possibly launchAdjacent
+ // If we're replacing a pip-able app, we need to let mixed handler take care of
+ // it. Otherwise we'll just treat it as an enter+resize
+ if (mSplitLayout.calculateCurrentSnapPosition() != SNAP_TO_2_50_50) {
+ // updated layout will get applied in startAnimation pendingResize
+ mSplitTransitions.setEnterTransition(transition,
+ request.getRemoteTransition(),
+ TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, true /*resizeAnim*/);
+ }
} else if (inFullscreen && isSplitScreenVisible()) {
// If the trigger task is in fullscreen and in split, exit split and place
// task on top
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/OWNERS
index 28be0ef..9dc0ebb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/OWNERS
@@ -1,3 +1,2 @@
# WM shell sub-module TV splitscreen owner
-galinap@google.com
bronger@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 3652a16..bf58003 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -95,7 +95,6 @@
import android.util.ArrayMap;
import android.view.SurfaceControl;
import android.view.WindowManager;
-import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.window.TransitionInfo;
import android.window.TransitionMetrics;
@@ -835,9 +834,8 @@
a = mTransitionAnimation.loadVoiceActivityExitAnimation(enter, userId);
}
} else if (changeMode == TRANSIT_CHANGE) {
- // In the absence of a specific adapter, we just want to keep everything stationary.
- a = new AlphaAnimation(1.f, 1.f);
- a.setDuration(TransitionAnimation.DEFAULT_APP_TRANSITION_DURATION);
+ // Apply end state directly by default.
+ return null;
} else if (type == TRANSIT_RELAUNCH) {
a = mTransitionAnimation.createRelaunchAnimation(endBounds, mInsets, endBounds);
} else if (overrideType == ANIM_CUSTOM
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
index d054de4..cc37c44 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
@@ -26,7 +26,6 @@
import android.platform.test.annotations.EnableFlags
import android.provider.Settings
import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
-import android.testing.AndroidTestingRunner
import android.view.Display.DEFAULT_DISPLAY
import android.view.IWindowManager
import android.view.WindowManager.TRANSIT_CHANGE
@@ -43,6 +42,8 @@
import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
import com.android.wm.shell.transition.Transitions
import com.google.common.truth.Truth.assertThat
+import com.google.testing.junit.testparameterinjector.TestParameter
+import com.google.testing.junit.testparameterinjector.TestParameterInjector
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -62,7 +63,7 @@
* Usage: atest WMShellUnitTests:DesktopDisplayModeControllerTest
*/
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(TestParameterInjector::class)
class DesktopDisplayModeControllerTest : ShellTestCase() {
private val transitions = mock<Transitions>()
private val rootTaskDisplayAreaOrganizer = mock<RootTaskDisplayAreaOrganizer>()
@@ -143,41 +144,24 @@
@Test
@DisableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING)
- fun displayWindowingModeSwitchOnDisplayConnected_fullscreenDisplay_flagDisabled() {
+ fun displayWindowingModeSwitchOnDisplayConnected_flagDisabled(
+ @TestParameter param: ModeSwitchTestCase
+ ) {
testDisplayWindowingModeSwitch(
- defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
- extendedDisplayEnabled = true,
+ param.defaultWindowingMode,
+ param.extendedDisplayEnabled,
+ // When the flag is disabled, never switch.
expectToSwitch = false,
)
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING)
- fun displayWindowingModeSwitchOnDisplayConnected_extendedDisplayDisabled() {
+ fun displayWindowingModeSwitchOnDisplayConnected(@TestParameter param: ModeSwitchTestCase) {
testDisplayWindowingModeSwitch(
- defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
- extendedDisplayEnabled = false,
- expectToSwitch = false,
- )
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING)
- fun displayWindowingModeSwitchOnDisplayConnected_fullscreenDisplay() {
- testDisplayWindowingModeSwitch(
- defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
- extendedDisplayEnabled = true,
- expectToSwitch = true,
- )
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING)
- fun displayWindowingModeSwitchOnDisplayConnected_freeformDisplay() {
- testDisplayWindowingModeSwitch(
- defaultWindowingMode = WINDOWING_MODE_FREEFORM,
- extendedDisplayEnabled = true,
- expectToSwitch = false,
+ param.defaultWindowingMode,
+ param.extendedDisplayEnabled,
+ param.expectToSwitchByDefault,
)
}
@@ -249,7 +233,34 @@
}
}
- private companion object {
+ companion object {
const val EXTERNAL_DISPLAY_ID = 100
+
+ enum class ModeSwitchTestCase(
+ val defaultWindowingMode: Int,
+ val extendedDisplayEnabled: Boolean,
+ val expectToSwitchByDefault: Boolean,
+ ) {
+ FULLSCREEN_DISPLAY(
+ defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ extendedDisplayEnabled = true,
+ expectToSwitchByDefault = true,
+ ),
+ FULLSCREEN_DISPLAY_MIRRORING(
+ defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ extendedDisplayEnabled = false,
+ expectToSwitchByDefault = false,
+ ),
+ FREEFORM_DISPLAY(
+ defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+ extendedDisplayEnabled = true,
+ expectToSwitchByDefault = false,
+ ),
+ FREEFORM_DISPLAY_MIRRORING(
+ defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+ extendedDisplayEnabled = false,
+ expectToSwitchByDefault = false,
+ ),
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
index 50590f0..6b0ee5b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
@@ -56,7 +56,7 @@
}
@Test
- fun onTaskOpening_fullscreenTask_notActiveDesktopTask_noop() {
+ fun onTaskOpening_fullscreenTask_nonActiveDesktopTask_noop() {
val task = createFullscreenTask().apply { isVisible = true }
whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(false)
@@ -68,7 +68,7 @@
}
@Test
- fun onTaskOpening_freeformTask_activeDesktopTask_removesTaskFromRepo() {
+ fun onTaskOpening_fullscreenTask_taskIsActiveInDesktopRepo_removesTaskFromDesktopRepo() {
val task = createFullscreenTask().apply { isVisible = true }
whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
@@ -78,8 +78,19 @@
}
@Test
- fun onTaskOpening_freeformTask_visibleDesktopTask_addsTaskToRepository() {
+ fun onTaskOpening_freeformTask_activeInDesktopRepository_noop() {
val task = createFreeformTask().apply { isVisible = true }
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
+
+ desktopTaskChangeListener.onTaskOpening(task)
+
+ verify(desktopUserRepositories.current, never())
+ .addTask(task.displayId, task.taskId, task.isVisible)
+ }
+
+ @Test
+ fun onTaskOpening_freeformTask_notActiveInDesktopRepo_addsTaskToRepository() {
+ val task = createFreeformTask().apply { isVisible = false }
whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(false)
desktopTaskChangeListener.onTaskOpening(task)
@@ -88,17 +99,7 @@
}
@Test
- fun onTaskOpening_freeformTask_nonVisibleDesktopTask_addsTaskToRepository() {
- val task = createFreeformTask().apply { isVisible = false }
- whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
-
- desktopTaskChangeListener.onTaskOpening(task)
-
- verify(desktopUserRepositories.current).addTask(task.displayId, task.taskId, task.isVisible)
- }
-
- @Test
- fun onTaskChanging_freeformTaskOutsideDesktop_removesTaskFromRepo() {
+ fun onTaskChanging_fullscreenTask_activeInDesktopRepository_removesTaskFromRepo() {
val task = createFullscreenTask().apply { isVisible = true }
whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
@@ -108,7 +109,27 @@
}
@Test
- fun onTaskChanging_visibleTaskInDesktop_updatesTaskVisibility() {
+ fun onTaskChanging_fullscreenTask_nonActiveInDesktopRepo_noop() {
+ val task = createFullscreenTask().apply { isVisible = true }
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(false)
+
+ desktopTaskChangeListener.onTaskChanging(task)
+
+ verify(desktopUserRepositories.current, never()).removeTask(task.displayId, task.taskId)
+ }
+
+ @Test
+ fun onTaskChanging_freeformTask_nonActiveTaskInDesktopRepo_addsTaskToDesktopRepo() {
+ val task = createFreeformTask().apply { isVisible = true }
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(false)
+
+ desktopTaskChangeListener.onTaskChanging(task)
+
+ verify(desktopUserRepositories.current).addTask(task.displayId, task.taskId, task.isVisible)
+ }
+
+ @Test
+ fun onTaskChanging_freeformTask_activeVisibleTaskInDesktopRepo_updatesTaskVisibility() {
val task = createFreeformTask().apply { isVisible = true }
whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
@@ -119,7 +140,7 @@
}
@Test
- fun onTaskChanging_nonVisibleTask_updatesTaskVisibility() {
+ fun onTaskChanging_freeformTask_activeNonVisibleTask_updatesTaskVisibility() {
val task = createFreeformTask().apply { isVisible = false }
whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
@@ -130,7 +151,7 @@
}
@Test
- fun onTaskMovingToFront_freeformTaskOutsideDesktop_removesTaskFromRepo() {
+ fun onTaskMovingToFront_fullscreenTask_activeTaskInDesktopRepo_removesTaskFromRepo() {
val task = createFullscreenTask().apply { isVisible = true }
whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
@@ -140,9 +161,18 @@
}
@Test
- fun onTaskMovingToFront_freeformTaskOutsideDesktop_addsTaskToRepo() {
+ fun onTaskMovingToFront_fullscreenTask_nonActiveTaskInDesktopRepo_noop() {
val task = createFullscreenTask().apply { isVisible = true }
- whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(false)
+
+ desktopTaskChangeListener.onTaskMovingToFront(task)
+
+ verify(desktopUserRepositories.current, never()).removeTask(task.displayId, task.taskId)
+ }
+
+ @Test
+ fun onTaskMovingToFront_freeformTask_addsTaskToRepo() {
+ val task = createFreeformTask().apply { isVisible = true }
desktopTaskChangeListener.onTaskMovingToFront(task)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 8805071..b0785df 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -334,6 +334,16 @@
whenever(enterDesktopTransitionHandler.moveToDesktop(any(), any())).thenAnswer { Binder() }
whenever(exitDesktopTransitionHandler.startTransition(any(), any(), any(), any()))
.thenReturn(Binder())
+ whenever(
+ desktopMixedTransitionHandler.startLaunchTransition(
+ any(),
+ any(),
+ anyOrNull(),
+ anyOrNull(),
+ anyOrNull(),
+ )
+ )
+ .thenReturn(Binder())
whenever(displayController.getDisplayLayout(anyInt())).thenReturn(displayLayout)
whenever(displayController.getDisplayContext(anyInt())).thenReturn(mockDisplayContext)
whenever(displayController.getDisplay(anyInt())).thenReturn(display)
@@ -1616,7 +1626,7 @@
@Test
@DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun moveTaskToDesktop_desktopWallpaperDisabled_nonRunningTask_launchesInFreeform() {
- val task = createTaskInfo(1)
+ val task = createRecentTaskInfo(1)
whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
@@ -1631,7 +1641,7 @@
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun moveTaskToDesktop_desktopWallpaperEnabled_nonRunningTask_launchesInFreeform() {
whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
- val task = createTaskInfo(1)
+ val task = createRecentTaskInfo(1)
whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
@@ -1803,7 +1813,7 @@
whenever(transitions.startTransition(anyInt(), any(), transitionHandlerArgCaptor.capture()))
.thenReturn(Binder())
- val task = createTaskInfo(1)
+ val task = createRecentTaskInfo(1)
whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
controller.moveTaskToDefaultDeskAndActivate(
@@ -1818,6 +1828,34 @@
}
@Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+ Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY,
+ Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
+ )
+ fun moveBackgroundTaskToDesktop_nonDefaultDisplay_reordersHomeAndWallpaperOfNonDefaultDisplay() {
+ val homeTask = setUpHomeTask(displayId = SECOND_DISPLAY)
+ val wallpaperToken = MockToken().token()
+ whenever(desktopWallpaperActivityTokenProvider.getToken(SECOND_DISPLAY))
+ .thenReturn(wallpaperToken)
+ val task = setUpFreeformTask(displayId = SECOND_DISPLAY, deskId = 2, background = true)
+
+ controller.moveTaskToDefaultDeskAndActivate(
+ taskId = task.taskId,
+ transitionSource = UNKNOWN,
+ remoteTransition = RemoteTransition(spy(TestRemoteTransition())),
+ )
+
+ val wct = getLatestTransition()
+ val homeReorderIndex = wct.indexOfReorder(homeTask, toTop = true)
+ val wallpaperReorderIndex = wct.indexOfReorder(wallpaperToken, toTop = true)
+ assertThat(homeReorderIndex).isNotEqualTo(-1)
+ assertThat(wallpaperReorderIndex).isNotEqualTo(-1)
+ // Wallpaper last, to be in front of Home.
+ assertThat(wallpaperReorderIndex).isGreaterThan(homeReorderIndex)
+ }
+
+ @Test
fun moveRunningTaskToDesktop_remoteTransition_usesOneShotHandler() {
val transitionHandlerArgCaptor = argumentCaptor<TransitionHandler>()
whenever(transitions.startTransition(anyInt(), any(), transitionHandlerArgCaptor.capture()))
@@ -2468,7 +2506,7 @@
@Test
fun moveTaskToFront_backgroundTask_launchesTask() {
- val task = createTaskInfo(1)
+ val task = createRecentTaskInfo(1)
whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
whenever(
desktopMixedTransitionHandler.startLaunchTransition(
@@ -2490,7 +2528,7 @@
@Test
fun moveTaskToFront_backgroundTaskBringsTasksOverLimit_minimizesBackTask() {
val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
- val task = createTaskInfo(1001)
+ val task = createRecentTaskInfo(1001)
whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(null)
whenever(
desktopMixedTransitionHandler.startLaunchTransition(
@@ -2786,6 +2824,73 @@
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun moveToNextDisplay_toDesktopInOtherDisplay_bringsExistingTasksToFront() {
+ val transition = Binder()
+ val sourceDeskId = 0
+ val targetDeskId = 2
+ taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = targetDeskId)
+ taskRepository.setDeskInactive(deskId = targetDeskId)
+ // Set up two display ids
+ whenever(rootTaskDisplayAreaOrganizer.displayIds)
+ .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
+ // Create a mock for the target display area: second display
+ val secondDisplayArea = DisplayAreaInfo(MockToken().token(), SECOND_DISPLAY, 0)
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(SECOND_DISPLAY))
+ .thenReturn(secondDisplayArea)
+ whenever(transitions.startTransition(eq(TRANSIT_CHANGE), any(), anyOrNull()))
+ .thenReturn(transition)
+ val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = sourceDeskId)
+ val task2 = setUpFreeformTask(displayId = SECOND_DISPLAY, deskId = targetDeskId)
+
+ controller.moveToNextDisplay(task1.taskId)
+
+ // Existing desktop task in the target display is moved to front.
+ val wct = getLatestTransition()
+ wct.assertReorder(task2.token, /* toTop= */ true)
+ }
+
+ @Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+ Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY,
+ Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
+ )
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun moveToNextDisplay_toDesktopInOtherDisplay_movesHomeAndWallpaperToFront() {
+ val homeTask = setUpHomeTask(displayId = SECOND_DISPLAY)
+ whenever(desktopWallpaperActivityTokenProvider.getToken(SECOND_DISPLAY))
+ .thenReturn(wallpaperToken)
+ val transition = Binder()
+ val sourceDeskId = 0
+ val targetDeskId = 2
+ taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = targetDeskId)
+ taskRepository.setDeskInactive(deskId = targetDeskId)
+ // Set up two display ids
+ whenever(rootTaskDisplayAreaOrganizer.displayIds)
+ .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
+ // Create a mock for the target display area: second display
+ val secondDisplayArea = DisplayAreaInfo(MockToken().token(), SECOND_DISPLAY, 0)
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(SECOND_DISPLAY))
+ .thenReturn(secondDisplayArea)
+ whenever(transitions.startTransition(eq(TRANSIT_CHANGE), any(), anyOrNull()))
+ .thenReturn(transition)
+ val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = sourceDeskId)
+
+ controller.moveToNextDisplay(task1.taskId)
+
+ // Home / Wallpaper should be moved to front as the background of desktop tasks, otherwise
+ // fullscreen (non-desktop) tasks could remain visible.
+ val wct = getLatestTransition()
+ val homeReorderIndex = wct.indexOfReorder(homeTask, toTop = true)
+ val wallpaperReorderIndex = wct.indexOfReorder(wallpaperToken, toTop = true)
+ assertThat(homeReorderIndex).isNotEqualTo(-1)
+ assertThat(wallpaperReorderIndex).isNotEqualTo(-1)
+ // Wallpaper last, to be in front of Home.
+ assertThat(wallpaperReorderIndex).isGreaterThan(homeReorderIndex)
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun moveToNextDisplay_toDeskInOtherDisplay_movesToDeskAndActivates() {
val transition = Binder()
@@ -2859,6 +2964,35 @@
}
@Test
+ fun moveToNextDisplay_movingToDesktop_sendsTaskbarRoundingUpdate() {
+ val transition = Binder()
+ val sourceDeskId = 1
+ val targetDeskId = 2
+ taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = targetDeskId)
+ taskRepository.setDeskInactive(deskId = targetDeskId)
+ // Set up two display ids
+ whenever(rootTaskDisplayAreaOrganizer.displayIds)
+ .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY))
+ // Create a mock for the target display area: second display
+ val secondDisplayArea = DisplayAreaInfo(MockToken().token(), SECOND_DISPLAY, 0)
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(SECOND_DISPLAY))
+ .thenReturn(secondDisplayArea)
+ whenever(transitions.startTransition(eq(TRANSIT_CHANGE), any(), anyOrNull()))
+ .thenReturn(transition)
+
+ val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = sourceDeskId)
+ taskRepository.addTaskToDesk(
+ displayId = DEFAULT_DISPLAY,
+ deskId = sourceDeskId,
+ taskId = task.taskId,
+ isVisible = true,
+ )
+ controller.moveToNextDisplay(task.taskId)
+
+ verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(anyBoolean())
+ }
+
+ @Test
fun getTaskWindowingMode() {
val fullscreenTask = setUpFullscreenTask()
val freeformTask = setUpFreeformTask()
@@ -6452,6 +6586,25 @@
@EnableFlags(
Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ )
+ fun startLaunchTransition_desktopNotShowing_updatesDesktopEnterExitListener() {
+ setUpFreeformTask(displayId = DEFAULT_DISPLAY, deskId = 0)
+ taskRepository.setDeskInactive(deskId = 0)
+
+ controller.startLaunchTransition(
+ transitionType = TRANSIT_OPEN,
+ wct = WindowContainerTransaction(),
+ launchingTaskId = null,
+ )
+
+ verify(desktopModeEnterExitTransitionListener).onEnterDesktopModeTransitionStarted(any())
+ }
+
+ @Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+ Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
)
fun startLaunchTransition_desktopShowing_doesNotReorderWallpaper() {
val wct = WindowContainerTransaction()
@@ -6620,7 +6773,7 @@
if (background) {
whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(null)
whenever(recentTasksController.findTaskInBackground(task.taskId))
- .thenReturn(createTaskInfo(task.taskId))
+ .thenReturn(createRecentTaskInfo(taskId = task.taskId, displayId = displayId))
} else {
whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
}
@@ -6791,6 +6944,12 @@
return arg.lastValue
}
+ private fun getLatestTransition(): WindowContainerTransaction {
+ val arg = argumentCaptor<WindowContainerTransaction>()
+ verify(transitions).startTransition(any(), arg.capture(), anyOrNull())
+ return arg.lastValue
+ }
+
private fun getLatestEnterDesktopWct(): WindowContainerTransaction {
val arg = argumentCaptor<WindowContainerTransaction>()
verify(enterDesktopTransitionHandler).moveToDesktop(arg.capture(), any())
@@ -7049,8 +7208,9 @@
} ?: false
}
-private fun createTaskInfo(id: Int) =
+private fun createRecentTaskInfo(taskId: Int, displayId: Int = DEFAULT_DISPLAY) =
RecentTaskInfo().apply {
- taskId = id
+ this.taskId = taskId
+ this.displayId = displayId
token = WindowContainerToken(mock(IWindowContainerToken::class.java))
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/OWNERS b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/OWNERS
index 736d4cf..a7d1890 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/OWNERS
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/OWNERS
@@ -1,3 +1,2 @@
# WM shell sub-module TV pip owners
-galinap@google.com
-bronger@google.com
\ No newline at end of file
+bronger@google.com
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
index 414c014..ffef0d1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
@@ -16,6 +16,8 @@
package com.android.wm.shell.splitscreen;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50;
+
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -58,6 +60,7 @@
doReturn(leash).when(out).getDividerLeash();
doReturn(bounds1).when(out).getTopLeftBounds();
doReturn(bounds2).when(out).getBottomRightBounds();
+ doReturn(SNAP_TO_2_50_50).when(out).calculateCurrentSnapPosition();
return out;
}
diff --git a/libs/hwui/OWNERS b/libs/hwui/OWNERS
index 70d13ab..9c06fd5 100644
--- a/libs/hwui/OWNERS
+++ b/libs/hwui/OWNERS
@@ -3,7 +3,7 @@
alecmouri@google.com
djsollen@google.com
jreck@google.com
-njawad@google.com
+nscobie@google.com
sumir@google.com
# For text, e.g. Typeface, Font, Minikin, etc.
diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
index fb89973..66de8c7 100644
--- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -27,8 +27,6 @@
import android.telephony.emergency.EmergencyNumber;
import android.util.Log;
-import com.android.internal.annotations.KeepForWeakReference;
-
import java.util.concurrent.TimeUnit;
/**
@@ -94,7 +92,6 @@
// The internal implementation of TelephonyManager uses WeakReference so we have to keep a
// reference here.
- @KeepForWeakReference
private final EmergencyCallListener mEmergencyCallListener = new EmergencyCallListener();
private final EmergencyCallCallback mEmergencyCallCallback;
diff --git a/media/OWNERS b/media/OWNERS
index 5e39195..50995ea 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -18,5 +18,4 @@
include platform/frameworks/av:/media/janitors/media_solutions_OWNERS
# SEA/KIR/BVE
-jtinker@google.com
robertshih@google.com
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 9bb31d0..4e86eac 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -1223,8 +1223,14 @@
private static final String TAG = "CodecCapabilities";
// NEW-STYLE CAPABILITIES
+ // Capabilities for an audio codec.
+ @Nullable
private AudioCapabilities mAudioCaps;
+ // Capabilities for a video codec.
+ @Nullable
private VideoCapabilities mVideoCaps;
+ // Capabilities specific to an encoder (vs. a decoder).
+ @Nullable
private EncoderCapabilities mEncoderCaps;
private MediaFormat mDefaultFormat;
@@ -1262,6 +1268,7 @@
/**
* Returns the audio capabilities or {@code null} if this is not an audio codec.
*/
+ @Nullable
public AudioCapabilities getAudioCapabilities() {
return mAudioCaps;
}
@@ -1273,6 +1280,7 @@
/**
* Returns the encoding capabilities or {@code null} if this is not an encoder.
*/
+ @Nullable
public EncoderCapabilities getEncoderCapabilities() {
return mEncoderCaps;
}
@@ -1284,6 +1292,7 @@
/**
* Returns the video capabilities or {@code null} if this is not a video codec.
*/
+ @Nullable
public VideoCapabilities getVideoCapabilities() {
return mVideoCaps;
}
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index b11a810..4e1d472 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -375,6 +375,9 @@
/**
* Extract DRM initialization data if it exists
*
+ * <p>If the media contains a PSSH box, only PSSH version 0 is supported. The result for media
+ * with other PSSH versions is undefined.
+ *
* @return DRM initialization data in the content, or {@code null}
* if no recognizable DRM format is found;
* @see DrmInitData
@@ -460,6 +463,10 @@
/**
* Get the PSSH info if present.
+ *
+ * <p>This method only supports version 0 PSSH boxes. The result for other versions is
+ * undefined.
+ *
* @return a map of uuid-to-bytes, with the uuid specifying
* the crypto scheme, and the bytes being the data specific to that scheme.
* This can be {@code null} if the source does not contain PSSH info.
diff --git a/media/java/android/media/Utils.java b/media/java/android/media/Utils.java
index d6e27b0..9db527b 100644
--- a/media/java/android/media/Utils.java
+++ b/media/java/android/media/Utils.java
@@ -18,6 +18,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.TestApi;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
@@ -61,16 +63,30 @@
*
* @hide
*/
+@TestApi
+@SuppressLint({"UnflaggedApi", "StaticUtils"}) // Test API
public class Utils {
private static final String TAG = "Utils";
+ /** @hide
+ * The vibration uri key parameter
+ */
+ @TestApi
+ @SuppressLint("UnflaggedApi") // Test API
public static final String VIBRATION_URI_PARAM = "vibration_uri";
+ /** @hide
+ * Indicates the synchronized vibration
+ */
+ @TestApi
+ @SuppressLint("UnflaggedApi") // Test API
public static final String SYNCHRONIZED_VIBRATION = "synchronized";
/**
* Sorts distinct (non-intersecting) range array in ascending order.
* @throws java.lang.IllegalArgumentException if ranges are not distinct
+ *
+ * @hide
*/
public static <T extends Comparable<? super T>> void sortDistinctRanges(Range<T>[] ranges) {
Arrays.sort(ranges, new Comparator<Range<T>>() {
@@ -92,6 +108,8 @@
* @param one a sorted set of non-intersecting ranges in ascending order
* @param another another sorted set of non-intersecting ranges in ascending order
* @return the intersection of the two sets, sorted in ascending order
+ *
+ * @hide
*/
public static <T extends Comparable<? super T>>
Range<T>[] intersectSortedDistinctRanges(Range<T>[] one, Range<T>[] another) {
@@ -124,6 +142,8 @@
* @return if the value is in one of the ranges, it returns the index of that range. Otherwise,
* the return value is {@code (-1-index)} for the {@code index} of the range that is
* immediately following {@code value}.
+ *
+ * @hide
*/
public static <T extends Comparable<? super T>>
int binarySearchDistinctRanges(Range<T>[] ranges, T value) {
@@ -358,6 +378,8 @@
* @param fileName desired name for the file.
* @param mimeType MIME type of the file to create.
* @return the File object in the storage, or null if an error occurs.
+ *
+ * @hide
*/
public static File getUniqueExternalFile(Context context, String subdirectory, String fileName,
String mimeType) {
@@ -676,6 +698,8 @@
* Must match the implementation of BluetoothUtils.toAnonymizedAddress()
* @param address MAC address to be anonymized
* @return anonymized MAC address
+ *
+ * @hide
*/
public static @Nullable String anonymizeBluetoothAddress(@Nullable String address) {
if (address == null) {
@@ -693,6 +717,8 @@
* @param deviceType the internal type of the audio device
* @param address MAC address to be anonymized
* @return anonymized MAC address
+ *
+ * @hide
*/
public static @Nullable String anonymizeBluetoothAddress(
int deviceType, @Nullable String address) {
@@ -707,6 +733,8 @@
*
* @param context the {@link Context}
* @return {@code true} if the device supports ringtone vibration
+ *
+ * @hide
*/
public static boolean isRingtoneVibrationSettingsSupported(Context context) {
final Resources res = context.getResources();
@@ -719,6 +747,8 @@
*
* @param ringtoneUri the ringtone Uri
* @return {@code true} if the Uri has vibration parameter
+ *
+ * @hide
*/
public static boolean hasVibration(Uri ringtoneUri) {
if (ringtoneUri == null) {
@@ -734,6 +764,8 @@
* @param ringtoneUri the ringtone Uri
* @return parsed {@link Uri} of vibration parameter, {@code null} if the vibration parameter
* is not found.
+ *
+ * @hide
*/
public static @Nullable Uri getVibrationUri(Uri ringtoneUri) {
if (ringtoneUri == null) {
@@ -751,6 +783,8 @@
*
* @param vibrator the vibrator to resolve the vibration file
* @param vibrationUri the vibration file Uri to represent a vibration
+ *
+ * @hide
*/
@SuppressWarnings("FlaggedApi") // VibrationXmlParser is available internally as hidden APIs.
public static VibrationEffect parseVibrationEffect(Vibrator vibrator, Uri vibrationUri) {
diff --git a/media/java/android/media/musicrecognition/OWNERS b/media/java/android/media/musicrecognition/OWNERS
index 037b048..820be00 100644
--- a/media/java/android/media/musicrecognition/OWNERS
+++ b/media/java/android/media/musicrecognition/OWNERS
@@ -1,5 +1,4 @@
# Bug component: 830636
oni@google.com
-volnov@google.com
diff --git a/media/java/android/media/projection/MediaProjectionConfig.java b/media/java/android/media/projection/MediaProjectionConfig.java
index 30f34fe..598b534 100644
--- a/media/java/android/media/projection/MediaProjectionConfig.java
+++ b/media/java/android/media/projection/MediaProjectionConfig.java
@@ -27,7 +27,6 @@
import android.os.Parcelable;
import com.android.internal.util.AnnotationValidations;
-import com.android.internal.util.DataClass;
import java.lang.annotation.Retention;
@@ -35,36 +34,25 @@
* Configure the {@link MediaProjection} session requested from
* {@link MediaProjectionManager#createScreenCaptureIntent(MediaProjectionConfig)}.
*/
-@DataClass(
- genEqualsHashCode = true,
- genAidl = true,
- genSetters = false,
- genConstructor = false,
- genBuilder = false,
- genToString = false,
- genHiddenConstDefs = true,
- genHiddenGetters = true,
- genConstDefs = false
-)
public final class MediaProjectionConfig implements Parcelable {
/**
* The user, rather than the host app, determines which region of the display to capture.
+ *
* @hide
*/
public static final int CAPTURE_REGION_USER_CHOICE = 0;
/**
* The host app specifies a particular display to capture.
+ *
* @hide
*/
public static final int CAPTURE_REGION_FIXED_DISPLAY = 1;
/** @hide */
- @IntDef(prefix = "CAPTURE_REGION_", value = {
- CAPTURE_REGION_USER_CHOICE,
- CAPTURE_REGION_FIXED_DISPLAY
- })
+ @IntDef(prefix = "CAPTURE_REGION_", value = {CAPTURE_REGION_USER_CHOICE,
+ CAPTURE_REGION_FIXED_DISPLAY})
@Retention(SOURCE)
public @interface CaptureRegion {
}
@@ -72,7 +60,7 @@
/**
* The particular display to capture. Only used when {@link #getRegionToCapture()} is
* {@link #CAPTURE_REGION_FIXED_DISPLAY}; ignored otherwise.
- *
+ * <p>
* Only supports values of {@link android.view.Display#DEFAULT_DISPLAY}.
*/
@IntRange(from = DEFAULT_DISPLAY, to = DEFAULT_DISPLAY)
@@ -82,13 +70,7 @@
* The region to capture. Defaults to the user's choice.
*/
@CaptureRegion
- private int mRegionToCapture = CAPTURE_REGION_USER_CHOICE;
-
- /**
- * Default instance, with region set to the user's choice.
- */
- private MediaProjectionConfig() {
- }
+ private int mRegionToCapture;
/**
* Customized instance, with region set to the provided value.
@@ -129,51 +111,29 @@
*/
@NonNull
private static String captureRegionToString(int value) {
- switch (value) {
- case CAPTURE_REGION_USER_CHOICE:
- return "CAPTURE_REGION_USERS_CHOICE";
- case CAPTURE_REGION_FIXED_DISPLAY:
- return "CAPTURE_REGION_GIVEN_DISPLAY";
- default:
- return Integer.toHexString(value);
- }
+ return switch (value) {
+ case CAPTURE_REGION_USER_CHOICE -> "CAPTURE_REGION_USERS_CHOICE";
+ case CAPTURE_REGION_FIXED_DISPLAY -> "CAPTURE_REGION_GIVEN_DISPLAY";
+ default -> Integer.toHexString(value);
+ };
}
@Override
public String toString() {
- return "MediaProjectionConfig { "
- + "displayToCapture = " + mDisplayToCapture + ", "
- + "regionToCapture = " + captureRegionToString(mRegionToCapture)
- + " }";
+ return "MediaProjectionConfig { " + "displayToCapture = " + mDisplayToCapture + ", "
+ + "regionToCapture = " + captureRegionToString(mRegionToCapture) + " }";
}
-
-
-
- // Code below generated by codegen v1.0.23.
- //
- // DO NOT MODIFY!
- // CHECKSTYLE:OFF Generated code
- //
- // To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/media/java/android/media/projection/MediaProjectionConfig.java
- //
- // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
- // Settings > Editor > Code Style > Formatter Control
- //@formatter:off
-
-
/**
* The particular display to capture. Only used when {@link #getRegionToCapture()} is
* {@link #CAPTURE_REGION_FIXED_DISPLAY}; ignored otherwise.
- *
+ * <p>
* Only supports values of {@link android.view.Display#DEFAULT_DISPLAY}.
*
* @hide
*/
- @DataClass.Generated.Member
- public @IntRange(from = DEFAULT_DISPLAY, to = DEFAULT_DISPLAY) int getDisplayToCapture() {
+ public int getDisplayToCapture() {
return mDisplayToCapture;
}
@@ -182,34 +142,21 @@
*
* @hide
*/
- @DataClass.Generated.Member
public @CaptureRegion int getRegionToCapture() {
return mRegionToCapture;
}
@Override
- @DataClass.Generated.Member
public boolean equals(@Nullable Object o) {
- // You can override field equality logic by defining either of the methods like:
- // boolean fieldNameEquals(MediaProjectionConfig other) { ... }
- // boolean fieldNameEquals(FieldType otherValue) { ... }
-
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
- @SuppressWarnings("unchecked")
MediaProjectionConfig that = (MediaProjectionConfig) o;
- //noinspection PointlessBooleanExpression
- return true
- && mDisplayToCapture == that.mDisplayToCapture
+ return mDisplayToCapture == that.mDisplayToCapture
&& mRegionToCapture == that.mRegionToCapture;
}
@Override
- @DataClass.Generated.Member
public int hashCode() {
- // You can override field hashCode logic by defining methods like:
- // int fieldNameHashCode() { ... }
-
int _hash = 1;
_hash = 31 * _hash + mDisplayToCapture;
_hash = 31 * _hash + mRegionToCapture;
@@ -217,65 +164,36 @@
}
@Override
- @DataClass.Generated.Member
public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
- // You can override field parcelling by defining methods like:
- // void parcelFieldName(Parcel dest, int flags) { ... }
-
dest.writeInt(mDisplayToCapture);
dest.writeInt(mRegionToCapture);
}
@Override
- @DataClass.Generated.Member
- public int describeContents() { return 0; }
+ public int describeContents() {
+ return 0;
+ }
/** @hide */
- @SuppressWarnings({"unchecked", "RedundantCast"})
- @DataClass.Generated.Member
/* package-private */ MediaProjectionConfig(@NonNull android.os.Parcel in) {
- // You can override field unparcelling by defining methods like:
- // static FieldType unparcelFieldName(Parcel in) { ... }
-
int displayToCapture = in.readInt();
int regionToCapture = in.readInt();
- this.mDisplayToCapture = displayToCapture;
- AnnotationValidations.validate(
- IntRange.class, null, mDisplayToCapture,
- "from", DEFAULT_DISPLAY,
- "to", DEFAULT_DISPLAY);
- this.mRegionToCapture = regionToCapture;
- AnnotationValidations.validate(
- CaptureRegion.class, null, mRegionToCapture);
-
- // onConstructed(); // You can define this method to get a callback
+ mDisplayToCapture = displayToCapture;
+ mRegionToCapture = regionToCapture;
+ AnnotationValidations.validate(CaptureRegion.class, null, mRegionToCapture);
}
- @DataClass.Generated.Member
- public static final @NonNull Parcelable.Creator<MediaProjectionConfig> CREATOR
- = new Parcelable.Creator<MediaProjectionConfig>() {
- @Override
- public MediaProjectionConfig[] newArray(int size) {
- return new MediaProjectionConfig[size];
- }
+ public static final @NonNull Parcelable.Creator<MediaProjectionConfig> CREATOR =
+ new Parcelable.Creator<>() {
+ @Override
+ public MediaProjectionConfig[] newArray(int size) {
+ return new MediaProjectionConfig[size];
+ }
- @Override
- public MediaProjectionConfig createFromParcel(@NonNull android.os.Parcel in) {
- return new MediaProjectionConfig(in);
- }
- };
-
- @DataClass.Generated(
- time = 1673548980960L,
- codegenVersion = "1.0.23",
- sourceFile = "frameworks/base/media/java/android/media/projection/MediaProjectionConfig.java",
- inputSignatures = "public static final int CAPTURE_REGION_USER_CHOICE\npublic static final int CAPTURE_REGION_FIXED_DISPLAY\nprivate @android.annotation.IntRange int mDisplayToCapture\nprivate @android.media.projection.MediaProjectionConfig.CaptureRegion int mRegionToCapture\npublic static @android.annotation.NonNull android.media.projection.MediaProjectionConfig createConfigForDefaultDisplay()\npublic static @android.annotation.NonNull android.media.projection.MediaProjectionConfig createConfigForUserChoice()\nprivate static @android.annotation.NonNull java.lang.String captureRegionToString(int)\npublic @java.lang.Override java.lang.String toString()\nclass MediaProjectionConfig extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genSetters=false, genConstructor=false, genBuilder=false, genToString=false, genHiddenConstDefs=true, genHiddenGetters=true, genConstDefs=false)")
- @Deprecated
- private void __metadata() {}
-
-
- //@formatter:on
- // End of generated code
-
+ @Override
+ public MediaProjectionConfig createFromParcel(@NonNull android.os.Parcel in) {
+ return new MediaProjectionConfig(in);
+ }
+ };
}
diff --git a/media/jni/OWNERS b/media/jni/OWNERS
index fdddf13..84618a3 100644
--- a/media/jni/OWNERS
+++ b/media/jni/OWNERS
@@ -1,5 +1,5 @@
# extra for MTP related files
-per-file android_mtp_*.cpp=aprasath@google.com,anothermark@google.com,kumarashishg@google.com,sarup@google.com,jsharkey@android.com,jameswei@google.com,rmojumder@google.com
+per-file android_mtp_*.cpp=aprasath@google.com,anothermark@google.com,sarup@google.com,jsharkey@android.com,jameswei@google.com,rmojumder@google.com
# extra for TV related files
per-file android_media_tv_*=hgchen@google.com,quxiangfang@google.com
diff --git a/native/webview/TEST_MAPPING b/native/webview/TEST_MAPPING
index 3858059..c9b5476 100644
--- a/native/webview/TEST_MAPPING
+++ b/native/webview/TEST_MAPPING
@@ -17,15 +17,6 @@
"exclude-annotation": "android.test.FlakyTest"
}
]
- },
- {
- "name": "GtsWebViewHostTestCases",
- "keywords": ["internal"],
- "options": [
- {
- "exclude-annotation": "android.test.FlakyTest"
- }
- ]
}
]
}
diff --git a/opengl/java/android/opengl/OWNERS b/opengl/java/android/opengl/OWNERS
index e340bc6..4ec9e29 100644
--- a/opengl/java/android/opengl/OWNERS
+++ b/opengl/java/android/opengl/OWNERS
@@ -3,4 +3,3 @@
sumir@google.com
prahladk@google.com
ianelliott@google.com
-lpy@google.com
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v36/settingslib_expressive_collapsing_toolbar_content_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v36/settingslib_expressive_collapsing_toolbar_content_layout.xml
index 6221659..3db0ac6 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v36/settingslib_expressive_collapsing_toolbar_content_layout.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v36/settingslib_expressive_collapsing_toolbar_content_layout.xml
@@ -41,6 +41,7 @@
android:id="@+id/action_bar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
+ android:layout_marginStart="@dimen/settingslib_expressive_space_extrasmall4"
android:theme="?android:attr/actionBarTheme"
android:transitionName="shared_element_view"
app:layout_collapseMode="pin"/>
diff --git a/packages/SettingsLib/OWNERS b/packages/SettingsLib/OWNERS
index 04df308..7348920 100644
--- a/packages/SettingsLib/OWNERS
+++ b/packages/SettingsLib/OWNERS
@@ -8,6 +8,7 @@
evanlaird@google.com
jiannan@google.com
juliacr@google.com
+millchen@google.com
ykhung@google.com
# Exempt resource files (because they are in a flat directory and too hard to manage via OWNERS)
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background.xml
index 9aa0bc3..0446873 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background.xml
@@ -19,16 +19,12 @@
android:color="?android:colorControlHighlight">
<item
android:start="?android:attr/listPreferredItemPaddingStart"
- android:end="?android:attr/listPreferredItemPaddingEnd"
- android:top="2dp"
- android:bottom="16dp">
+ android:end="?android:attr/listPreferredItemPaddingEnd">
<shape android:shape="rectangle">
<solid
android:color="@color/settingslib_materialColorSurfaceBright" />
<corners
android:radius="@dimen/settingslib_preference_corner_radius" />
- <padding
- android:bottom="16dp"/>
</shape>
</item>
-</ripple>
\ No newline at end of file
+</ripple>
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom.xml
index 554cba5..25a936d 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom.xml
@@ -19,9 +19,7 @@
android:color="?android:colorControlHighlight">
<item
android:start="?android:attr/listPreferredItemPaddingStart"
- android:end="?android:attr/listPreferredItemPaddingEnd"
- android:top="2dp"
- android:bottom="16dp">
+ android:end="?android:attr/listPreferredItemPaddingEnd">
<shape android:shape="rectangle">
<solid
android:color="@color/settingslib_materialColorSurfaceBright" />
@@ -30,8 +28,6 @@
android:bottomLeftRadius="@dimen/settingslib_preference_corner_radius"
android:topRightRadius="4dp"
android:bottomRightRadius="@dimen/settingslib_preference_corner_radius" />
- <padding
- android:bottom="16dp"/>
</shape>
</item>
-</ripple>
\ No newline at end of file
+</ripple>
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom_highlighted.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom_highlighted.xml
index c0c0869..db2800e 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom_highlighted.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom_highlighted.xml
@@ -19,8 +19,7 @@
<item
android:bottom="16dp"
android:end="?android:attr/listPreferredItemPaddingEnd"
- android:start="?android:attr/listPreferredItemPaddingStart"
- android:top="2dp">
+ android:start="?android:attr/listPreferredItemPaddingStart">
<shape
android:shape="rectangle"
android:tint="?android:attr/colorAccent">
@@ -29,8 +28,7 @@
android:bottomRightRadius="@dimen/settingslib_preference_corner_radius"
android:topLeftRadius="4dp"
android:topRightRadius="4dp" />
- <padding android:bottom="16dp" />
<solid android:color="#42000000" />
</shape>
</item>
-</ripple>
\ No newline at end of file
+</ripple>
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom_selected.xml
index 543b237..98f95d92 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom_selected.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom_selected.xml
@@ -19,9 +19,7 @@
android:color="?android:colorControlHighlight">
<item
android:start="?android:attr/listPreferredItemPaddingStart"
- android:end="?android:attr/listPreferredItemPaddingEnd"
- android:top="2dp"
- android:bottom="16dp">
+ android:end="?android:attr/listPreferredItemPaddingEnd">
<shape android:shape="rectangle">
<solid
android:color="@color/settingslib_materialColorSurfaceContainer" />
@@ -30,8 +28,6 @@
android:bottomLeftRadius="@dimen/settingslib_preference_corner_radius"
android:topRightRadius="4dp"
android:bottomRightRadius="@dimen/settingslib_preference_corner_radius" />
- <padding
- android:bottom="16dp"/>
</shape>
</item>
-</ripple>
\ No newline at end of file
+</ripple>
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center.xml
index b89a0dd..c4286fd 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center.xml
@@ -19,8 +19,7 @@
android:color="?android:colorControlHighlight">
<item
android:start="?android:attr/listPreferredItemPaddingStart"
- android:end="?android:attr/listPreferredItemPaddingEnd"
- android:top="2dp">
+ android:end="?android:attr/listPreferredItemPaddingEnd">
<shape android:shape="rectangle">
<solid
android:color="@color/settingslib_materialColorSurfaceBright" />
@@ -28,4 +27,4 @@
android:radius="4dp" />
</shape>
</item>
-</ripple>
\ No newline at end of file
+</ripple>
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center_highlighted.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center_highlighted.xml
index 8099d9b..194cdb0 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center_highlighted.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center_highlighted.xml
@@ -18,8 +18,7 @@
android:color="?android:colorControlHighlight">
<item
android:end="?android:attr/listPreferredItemPaddingEnd"
- android:start="?android:attr/listPreferredItemPaddingStart"
- android:top="2dp">
+ android:start="?android:attr/listPreferredItemPaddingStart">
<shape
android:shape="rectangle"
android:tint="?android:attr/colorAccent">
@@ -27,4 +26,4 @@
<solid android:color="#42000000" />
</shape>
</item>
-</ripple>
\ No newline at end of file
+</ripple>
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center_selected.xml
index 6d2cd1a..8bc2f2f 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center_selected.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center_selected.xml
@@ -19,8 +19,7 @@
android:color="?android:colorControlHighlight">
<item
android:start="?android:attr/listPreferredItemPaddingStart"
- android:end="?android:attr/listPreferredItemPaddingEnd"
- android:top="2dp">
+ android:end="?android:attr/listPreferredItemPaddingEnd">
<shape android:shape="rectangle">
<solid
android:color="@color/settingslib_materialColorSurfaceContainer" />
@@ -28,4 +27,4 @@
android:radius="4dp" />
</shape>
</item>
-</ripple>
\ No newline at end of file
+</ripple>
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_highlighted.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_highlighted.xml
index a119a4a..2341661 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_highlighted.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_highlighted.xml
@@ -19,14 +19,12 @@
<item
android:bottom="16dp"
android:end="?android:attr/listPreferredItemPaddingEnd"
- android:start="?android:attr/listPreferredItemPaddingStart"
- android:top="2dp">
+ android:start="?android:attr/listPreferredItemPaddingStart">
<shape
android:shape="rectangle"
android:tint="?android:attr/colorAccent">
<corners android:radius="@dimen/settingslib_preference_corner_radius" />
- <padding android:bottom="16dp" />
<solid android:color="#42000000" />
</shape>
</item>
-</ripple>
\ No newline at end of file
+</ripple>
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_selected.xml
index bcdbf1d..99704f2d 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_selected.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_selected.xml
@@ -19,16 +19,12 @@
android:color="?android:colorControlHighlight">
<item
android:start="?android:attr/listPreferredItemPaddingStart"
- android:end="?android:attr/listPreferredItemPaddingEnd"
- android:top="2dp"
- android:bottom="16dp">
+ android:end="?android:attr/listPreferredItemPaddingEnd">
<shape android:shape="rectangle">
<solid
android:color="@color/settingslib_materialColorSurfaceContainer" />
<corners
android:radius="@dimen/settingslib_preference_corner_radius" />
- <padding
- android:bottom="16dp"/>
</shape>
</item>
-</ripple>
\ No newline at end of file
+</ripple>
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top.xml
index 7955e44..3a59386 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top.xml
@@ -19,8 +19,7 @@
android:color="?android:colorControlHighlight">
<item
android:start="?android:attr/listPreferredItemPaddingStart"
- android:end="?android:attr/listPreferredItemPaddingEnd"
- android:top="2dp">
+ android:end="?android:attr/listPreferredItemPaddingEnd">
<shape android:shape="rectangle">
<solid
android:color="@color/settingslib_materialColorSurfaceBright" />
@@ -31,4 +30,4 @@
android:bottomRightRadius="4dp" />
</shape>
</item>
-</ripple>
\ No newline at end of file
+</ripple>
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top_highlighted.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top_highlighted.xml
index 052eb01..edace29 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top_highlighted.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top_highlighted.xml
@@ -19,8 +19,7 @@
<item
android:color="?android:attr/colorAccent"
android:end="?android:attr/listPreferredItemPaddingEnd"
- android:start="?android:attr/listPreferredItemPaddingStart"
- android:top="2dp">
+ android:start="?android:attr/listPreferredItemPaddingStart">
<shape
android:shape="rectangle"
android:tint="?android:attr/colorAccent">
@@ -32,4 +31,4 @@
<solid android:color="#42000000" />
</shape>
</item>
-</ripple>
\ No newline at end of file
+</ripple>
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top_selected.xml
index d4b658c..b2d6d9d 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top_selected.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top_selected.xml
@@ -19,8 +19,7 @@
android:color="?android:colorControlHighlight">
<item
android:start="?android:attr/listPreferredItemPaddingStart"
- android:end="?android:attr/listPreferredItemPaddingEnd"
- android:top="2dp">
+ android:end="?android:attr/listPreferredItemPaddingEnd">
<shape android:shape="rectangle">
<solid
android:color="@color/settingslib_materialColorSurfaceContainer" />
@@ -31,4 +30,4 @@
android:bottomRightRadius="4dp" />
</shape>
</item>
-</ripple>
\ No newline at end of file
+</ripple>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
index 3ccbbc0..2d6b6cf 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
@@ -28,6 +28,7 @@
<item name="switchStyle">@style/SwitchCompat.SettingsLib</item>
<item name="android:progressBarStyleHorizontal">@style/HorizontalProgressBar.SettingsLib</item>
<item name="android:listDivider">@drawable/settingslib_list_divider</item>
+ <item name="android:colorBackground">@color/settingslib_materialColorSurfaceContainerLowest</item>
</style>
<style name="Theme.SettingsBase" parent="Theme.SettingsBase_v31" />
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/NormalPaddingMixin.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/NormalPaddingMixin.kt
new file mode 100644
index 0000000..5035542
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/NormalPaddingMixin.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget
+
+/**
+ * A base interface to indicate that a Preference should have normal paddings.
+ *
+ * Preferences implementing this interface will be treated as has normal paddings both inside and
+ * outside.
+ */
+interface NormalPaddingMixin
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt
index 8d12f01..22cd873 100644
--- a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt
@@ -16,6 +16,7 @@
package com.android.settingslib.widget
+import android.graphics.Rect
import android.os.Bundle
import android.view.LayoutInflater;
import android.view.View
@@ -24,6 +25,7 @@
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceScreen
import androidx.recyclerview.widget.RecyclerView
+import com.android.settingslib.widget.theme.R
/** Base class for Settings to use PreferenceFragmentCompat */
abstract class SettingsBasePreferenceFragment : PreferenceFragmentCompat() {
@@ -43,6 +45,7 @@
if (SettingsThemeHelper.isExpressiveTheme(requireContext())) {
// Don't allow any divider in between the preferences in expressive design.
setDivider(null)
+ this.listView.addItemDecoration(MarginItemDecoration())
}
}
@@ -51,4 +54,18 @@
return SettingsPreferenceGroupAdapter(preferenceScreen)
return super.onCreateAdapter(preferenceScreen)
}
+
+ internal class MarginItemDecoration() : RecyclerView.ItemDecoration() {
+ override fun getItemOffsets(
+ outRect: Rect,
+ view: View,
+ parent: RecyclerView,
+ state: RecyclerView.State,
+ ) {
+ with(outRect) {
+ bottom =
+ view.resources.getDimensionPixelSize(R.dimen.settingslib_expressive_radius_extrasmall1)
+ }
+ }
+ }
}
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt
index a04fce7..2672787 100644
--- a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt
@@ -177,14 +177,32 @@
val v = holder.itemView
// Update padding
if (SettingsThemeHelper.isExpressiveTheme(context)) {
- val paddingStart = if (backgroundRes == 0) mNormalPaddingStart else mGroupPaddingStart
- val paddingEnd = if (backgroundRes == 0) mNormalPaddingEnd else mGroupPaddingEnd
+ val (paddingStart, paddingEnd) = getStartEndPadding(position, backgroundRes)
v.setPaddingRelative(paddingStart, v.paddingTop, paddingEnd, v.paddingBottom)
+ v.clipToOutline = backgroundRes != 0
}
// Update background
v.setBackgroundResource(backgroundRes)
}
+ private fun getStartEndPadding(position: Int, backgroundRes: Int): Pair<Int, Int> {
+ val item = getItem(position)
+ return when {
+ // This item handles edge to edge itself
+ item is NormalPaddingMixin && item is GroupSectionDividerMixin -> 0 to 0
+
+ // According to mappingPreferenceGroup(), backgroundRes == 0 means this item is
+ // GroupSectionDividerMixin or PreferenceCategory, which is design to have normal
+ // padding.
+ // NormalPaddingMixin items are also designed to have normal padding.
+ backgroundRes == 0 || item is NormalPaddingMixin ->
+ mNormalPaddingStart to mNormalPaddingEnd
+
+ // Other items are suppose to have group padding.
+ else -> mGroupPaddingStart to mGroupPaddingEnd
+ }
+ }
+
@DrawableRes
protected fun getRoundCornerDrawableRes(position: Int, isSelected: Boolean): Int {
return getRoundCornerDrawableRes(position, isSelected, false)
diff --git a/packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java b/packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java
index 4315238..fe8e8b6 100644
--- a/packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java
+++ b/packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java
@@ -22,6 +22,7 @@
import android.content.res.TypedArray;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
@@ -72,6 +73,7 @@
private int mSliderIncrement;
private boolean mAdjustable;
private boolean mTrackingTouch;
+ private CharSequence mSliderContentDescription;
/**
* Listener reacting to the user pressing DPAD left/right keys if {@code
@@ -143,6 +145,7 @@
@Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setLayoutResource(R.layout.settingslib_expressive_preference_slider);
+ setSelectable(false);
TypedArray a = context.obtainStyledAttributes(
attrs, androidx.preference.R.styleable.SeekBarPreference, defStyleAttr,
@@ -265,6 +268,14 @@
} else {
mSliderIncrement = (int) (mSlider.getStepSize());
}
+ final CharSequence title = getTitle();
+ if (!TextUtils.isEmpty(mSliderContentDescription)) {
+ mSlider.setContentDescription(mSliderContentDescription);
+ } else if (!TextUtils.isEmpty(title)) {
+ mSlider.setContentDescription(title);
+ } else {
+ mSlider.setContentDescription(null);
+ }
mSlider.setValueFrom(mMin);
mSlider.setValueTo(mMax);
mSlider.setValue(mSliderValue);
@@ -273,6 +284,8 @@
mSlider.clearOnChangeListeners();
mSlider.addOnChangeListener(mChangeListener);
mSlider.setEnabled(isEnabled());
+ mSlider.setFocusable(isSelectable());
+ mSlider.setClickable(isSelectable());
// Set up slider color
mSlider.setTrackActiveTintList(mTrackActiveColor);
@@ -471,6 +484,19 @@
setValueInternal(sliderValue, true);
}
+
+ /**
+ * Sets the content description of the {@link Slider}.
+ *
+ * @param contentDescription The content description of the {@link Slider}
+ */
+ public void setSliderContentDescription(@Nullable CharSequence contentDescription) {
+ mSliderContentDescription = contentDescription;
+ if (mSlider != null) {
+ mSlider.setContentDescription(contentDescription);
+ }
+ }
+
@Override
protected void onSetInitialValue(@Nullable Object defaultValue) {
if (defaultValue == null) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS b/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS
index b9449ac..50bfe8c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS
@@ -1,6 +1,5 @@
# Default reviewers for this and subdirectories.
andychou@google.com
-arcwang@google.com
asapperstein@google.com
changbetty@google.com
qal@google.com
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index c98a741..99c4e21c 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -456,11 +456,16 @@
@GuardedBy("mLock")
private void loadAconfigDefaultValuesLocked(List<String> filePaths) {
for (String fileName : filePaths) {
- try (FileInputStream inputStream = new FileInputStream(fileName)) {
- loadAconfigDefaultValues(
- inputStream.readAllBytes(), mNamespaceDefaults, mAconfigDefaultFlags);
- } catch (IOException e) {
- Slog.e(LOG_TAG, "failed to read protobuf", e);
+ File f = new File(fileName);
+ if (f.isFile() && f.canRead()) {
+ try (FileInputStream inputStream = new FileInputStream(fileName)) {
+ loadAconfigDefaultValues(
+ inputStream.readAllBytes(), mNamespaceDefaults, mAconfigDefaultFlags);
+ } catch (IOException e) {
+ Slog.e(LOG_TAG, "failed to read protobuf", e);
+ }
+ } else {
+ Slog.d(LOG_TAG, "No protobuf file at " + fileName);
}
}
}
diff --git a/packages/Shell/OWNERS b/packages/Shell/OWNERS
index 576afdc..2fa707e 100644
--- a/packages/Shell/OWNERS
+++ b/packages/Shell/OWNERS
@@ -7,9 +7,7 @@
svetoslavganov@google.com
hackbod@google.com
yamasani@google.com
-toddke@google.com
patb@google.com
-cbrubaker@google.com
omakoto@google.com
michaelwr@google.com
ronish@google.com
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index f2c76ba..7e8d549 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -203,6 +203,16 @@
}
flag {
+ name: "notifications_hun_shared_animation_values"
+ namespace: "systemui"
+ description: "Adds a shared class for fetching HUN animation values."
+ bug: "393369891"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "notification_undo_guts_on_config_changed"
namespace: "systemui"
description: "Fixes a bug where a theme or font change while notification guts were open"
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ComposableControllerFactory.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ComposableControllerFactory.kt
new file mode 100644
index 0000000..c842159
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ComposableControllerFactory.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.animation
+
+import android.content.ComponentName
+import android.util.Log
+import com.android.systemui.animation.ActivityTransitionAnimator.Controller
+import com.android.systemui.animation.ActivityTransitionAnimator.ControllerFactory
+import kotlinx.coroutines.flow.MutableStateFlow
+
+private const val TAG = "ComposableControllerFactory"
+
+/**
+ * [ControllerFactory] extension for Compose. Since composables are not guaranteed to be part of the
+ * composition when [ControllerFactory.createController] is called, this class provides a way for
+ * the composable to register itself at the time of composition, and deregister itself when
+ * disposed.
+ */
+abstract class ComposableControllerFactory(
+ cookie: ActivityTransitionAnimator.TransitionCookie,
+ component: ComponentName?,
+ launchCujType: Int? = null,
+ returnCujType: Int? = null,
+) : ControllerFactory(cookie, component, launchCujType, returnCujType) {
+ /**
+ * The object to be used to create [Controller]s, when its associate composable is in the
+ * composition.
+ */
+ protected val expandable = MutableStateFlow<Expandable?>(null)
+
+ /** To be called when the composable to be animated enters composition. */
+ fun onCompose(expandable: Expandable) {
+ if (TransitionAnimator.DEBUG) {
+ Log.d(TAG, "Composable entered composition (expandable=$expandable")
+ }
+ this.expandable.value = expandable
+ }
+
+ /** To be called when the composable to be animated exits composition. */
+ fun onDispose() {
+ if (TransitionAnimator.DEBUG) {
+ Log.d(TAG, "Composable left composition (expandable=${this.expandable.value}")
+ }
+ this.expandable.value = null
+ }
+}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
index 1e3c4c9..a352b1e 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
@@ -84,6 +84,7 @@
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
import com.android.compose.modifiers.thenIf
import com.android.compose.ui.graphics.FullScreenComposeViewInOverlay
+import com.android.systemui.animation.ComposableControllerFactory
import com.android.systemui.animation.Expandable
import com.android.systemui.animation.TransitionAnimator
import kotlin.math.max
@@ -119,6 +120,10 @@
* }
* ```
*
+ * [transitionControllerFactory] must be defined when this [Expandable] is registered for a
+ * long-term launch or return animation, to ensure that animation controllers can be created
+ * correctly.
+ *
* @sample com.android.systemui.compose.gallery.ActivityLaunchScreen
* @sample com.android.systemui.compose.gallery.DialogLaunchScreen
*/
@@ -134,10 +139,17 @@
// TODO(b/285250939): Default this to true then remove once the Compose QS expandables have
// proven that the new implementation is robust.
useModifierBasedImplementation: Boolean = false,
+ transitionControllerFactory: ComposableControllerFactory? = null,
content: @Composable (Expandable) -> Unit,
) {
Expandable(
- rememberExpandableController(color, shape, contentColor, borderStroke),
+ rememberExpandableController(
+ color,
+ shape,
+ contentColor,
+ borderStroke,
+ transitionControllerFactory,
+ ),
modifier,
onClick,
interactionSource,
@@ -183,6 +195,17 @@
) {
val controller = controller as ExpandableControllerImpl
+ if (controller.transitionControllerFactory != null) {
+ DisposableEffect(controller.transitionControllerFactory) {
+ // Notify the transition controller factory that the expandable is now available, so it
+ // can move forward with any pending requests.
+ controller.transitionControllerFactory.onCompose(controller.expandable)
+ // Once this composable is gone, the transition controller factory must be notified so
+ // it doesn't accepts requests providing stale content.
+ onDispose { controller.transitionControllerFactory.onDispose() }
+ }
+ }
+
if (useModifierBasedImplementation) {
Box(modifier.expandable(controller, onClick, interactionSource)) {
WrappedContent(controller.expandable, controller.contentColor, content)
@@ -308,34 +331,28 @@
interactionSource: MutableInteractionSource? = null,
): Modifier {
val controller = controller as ExpandableControllerImpl
+ val graphicsLayer = rememberGraphicsLayer()
val isAnimating = controller.isAnimating
- val drawInOverlayModifier =
- if (isAnimating) {
- val graphicsLayer = rememberGraphicsLayer()
-
- FullScreenComposeViewInOverlay(controller.overlay) { view ->
- Modifier.then(DrawExpandableInOverlayElement(view, controller, graphicsLayer))
- }
-
- Modifier.drawWithContent { graphicsLayer.record { this@drawWithContent.drawContent() } }
- } else {
- null
+ if (isAnimating) {
+ FullScreenComposeViewInOverlay(controller.overlay) { view ->
+ Modifier.then(DrawExpandableInOverlayElement(view, controller, graphicsLayer))
}
+ }
+ val drawContent = !isAnimating && !controller.isDialogShowing
return this.thenIf(onClick != null) { Modifier.minimumInteractiveComponentSize() }
- .thenIf(!isAnimating) {
+ .thenIf(drawContent) {
Modifier.border(controller)
.then(clickModifier(controller, onClick, interactionSource))
.background(controller.color, controller.shape)
}
- .thenIf(drawInOverlayModifier != null) { drawInOverlayModifier!! }
.onPlaced { controller.boundsInComposeViewRoot = it.boundsInRoot() }
- .thenIf(!isAnimating && controller.isDialogShowing) {
- Modifier.layout { measurable, constraints ->
- measurable.measure(constraints).run {
- layout(width, height) { /* Do not place/draw. */ }
- }
+ .drawWithContent {
+ graphicsLayer.record { this@drawWithContent.drawContent() }
+
+ if (drawContent) {
+ drawLayer(graphicsLayer)
}
}
}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
index a03c896..72da175e 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
@@ -47,6 +47,7 @@
import androidx.compose.ui.unit.LayoutDirection
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.animation.ActivityTransitionAnimator
+import com.android.systemui.animation.ComposableControllerFactory
import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
@@ -77,6 +78,7 @@
shape: Shape,
contentColor: Color = contentColorFor(color),
borderStroke: BorderStroke? = null,
+ transitionControllerFactory: ComposableControllerFactory? = null,
): ExpandableController {
val composeViewRoot = LocalView.current
val density = LocalDensity.current
@@ -95,6 +97,7 @@
composeViewRoot,
density,
layoutDirection,
+ transitionControllerFactory,
) {
ExpandableControllerImpl(
color,
@@ -103,6 +106,7 @@
borderStroke,
composeViewRoot,
density,
+ transitionControllerFactory,
layoutDirection,
{ isComposed },
)
@@ -127,6 +131,7 @@
internal val borderStroke: BorderStroke?,
internal val composeViewRoot: View,
internal val density: Density,
+ internal val transitionControllerFactory: ComposableControllerFactory?,
private val layoutDirection: LayoutDirection,
private val isComposed: () -> Boolean,
) : ExpandableController {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
index 2c6d09a..2d03e2b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
@@ -23,7 +23,6 @@
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
-import androidx.compose.ui.layout.Measurable
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntRect
@@ -35,7 +34,6 @@
import com.android.systemui.communal.ui.compose.section.AmbientStatusBarSection
import com.android.systemui.communal.ui.compose.section.CommunalLockSection
import com.android.systemui.communal.ui.compose.section.CommunalPopupSection
-import com.android.systemui.communal.ui.compose.section.CommunalToDreamButtonSection
import com.android.systemui.communal.ui.compose.section.HubOnboardingSection
import com.android.systemui.communal.ui.view.layout.sections.CommunalAppWidgetSection
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
@@ -61,7 +59,6 @@
private val ambientStatusBarSection: AmbientStatusBarSection,
private val communalPopupSection: CommunalPopupSection,
private val widgetSection: CommunalAppWidgetSection,
- private val communalToDreamButtonSection: CommunalToDreamButtonSection,
private val hubOnboardingSection: HubOnboardingSection,
) {
@@ -103,13 +100,11 @@
Modifier.element(Communal.Elements.IndicationArea).fillMaxWidth()
)
}
- with(communalToDreamButtonSection) { Button() }
},
) { measurables, constraints ->
val communalGridMeasurable = measurables[0]
val lockIconMeasurable = measurables[1]
val bottomAreaMeasurable = measurables[2]
- val screensaverButtonMeasurable: Measurable? = measurables.getOrNull(3)
val noMinConstraints = constraints.copy(minWidth = 0, minHeight = 0)
@@ -152,9 +147,6 @@
val bottomAreaPlaceable = bottomAreaMeasurable.measure(noMinConstraints)
- val screensaverButtonPlaceable =
- screensaverButtonMeasurable?.measure(noMinConstraints)
-
val communalGridPlaceable =
communalGridMeasurable.measure(
noMinConstraints.copy(maxHeight = lockIconBounds.top)
@@ -166,26 +158,12 @@
val bottomAreaTop = constraints.maxHeight - bottomAreaPlaceable.height
bottomAreaPlaceable.place(x = 0, y = bottomAreaTop)
-
- val screensaverButtonPaddingInt = screensaverButtonPadding.roundToPx()
- screensaverButtonPlaceable?.place(
- x =
- constraints.maxWidth -
- screensaverButtonPaddingInt -
- screensaverButtonPlaceable.width,
- y =
- constraints.maxHeight -
- screensaverButtonPaddingInt -
- screensaverButtonPlaceable.height,
- )
}
}
}
}
companion object {
- private val screensaverButtonPadding: Dp = 24.dp
-
// TODO(b/382739998): Remove these hardcoded values once lock icon size and bottom area
// position are sorted.
private val lockIconSize: Dp = 54.dp
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.kt
deleted file mode 100644
index acaf43a..0000000
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.kt
+++ /dev/null
@@ -1,231 +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.communal.ui.compose.section
-
-import androidx.compose.animation.AnimatedVisibility
-import androidx.compose.animation.core.MutableTransitionState
-import androidx.compose.animation.expandVertically
-import androidx.compose.animation.fadeIn
-import androidx.compose.animation.fadeOut
-import androidx.compose.animation.shrinkVertically
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.layout.widthIn
-import androidx.compose.foundation.shape.CornerSize
-import androidx.compose.material3.IconButtonDefaults
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.CornerRadius
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.RoundRect
-import androidx.compose.ui.geometry.Size
-import androidx.compose.ui.geometry.toRect
-import androidx.compose.ui.graphics.Outline
-import androidx.compose.ui.graphics.Path
-import androidx.compose.ui.graphics.Shape
-import androidx.compose.ui.input.pointer.pointerInput
-import androidx.compose.ui.res.dimensionResource
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.unit.Density
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.LayoutDirection
-import androidx.compose.ui.unit.dp
-import com.android.compose.PlatformIconButton
-import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
-import com.android.systemui.communal.ui.compose.extensions.observeTaps
-import com.android.systemui.communal.ui.viewmodel.CommunalToDreamButtonViewModel
-import com.android.systemui.lifecycle.rememberViewModel
-import com.android.systemui.res.R
-import javax.inject.Inject
-import kotlin.time.Duration.Companion.seconds
-import kotlinx.coroutines.delay
-
-class CommunalToDreamButtonSection
-@Inject
-constructor(
- private val communalSettingsInteractor: CommunalSettingsInteractor,
- private val viewModelFactory: CommunalToDreamButtonViewModel.Factory,
-) {
- @Composable
- fun Button() {
- if (!communalSettingsInteractor.isV2FlagEnabled()) {
- return
- }
-
- val viewModel =
- rememberViewModel("CommunalToDreamButtonSection") { viewModelFactory.create() }
-
- if (!viewModel.shouldShowDreamButtonOnHub) {
- return
- }
-
- val buttonSize = dimensionResource(R.dimen.communal_to_dream_button_size)
-
- if (viewModel.shouldShowTooltip) {
- val tooltipVisibleState = remember { MutableTransitionState(false) }
-
- Column(
- modifier =
- Modifier.widthIn(max = tooltipMaxWidth).pointerInput(Unit) {
- observeTaps {
- if (tooltipVisibleState.isCurrentlyVisible()) {
- tooltipVisibleState.targetState = false
- }
- }
- }
- ) {
- var waitingToShowTooltip by remember { mutableStateOf(true) }
-
- LaunchedEffect(tooltipVisibleState.targetState) {
- delay(3.seconds)
- tooltipVisibleState.targetState = true
- waitingToShowTooltip = false
- }
-
- // This LaunchedEffect is used to wait for the tooltip dismiss animation to
- // complete before setting the tooltip dismissed. Otherwise, the composable would
- // be removed before the animation can start.
- LaunchedEffect(
- tooltipVisibleState.currentState,
- tooltipVisibleState.isIdle,
- waitingToShowTooltip,
- ) {
- if (
- !waitingToShowTooltip &&
- !tooltipVisibleState.currentState &&
- tooltipVisibleState.isIdle
- ) {
- viewModel.setDreamButtonTooltipDismissed()
- }
- }
-
- AnimatedVisibility(
- visibleState = tooltipVisibleState,
- enter = fadeIn() + expandVertically(expandFrom = Alignment.Bottom),
- exit = fadeOut() + shrinkVertically(shrinkTowards = Alignment.Bottom),
- ) {
- Tooltip(
- pointerOffsetDp = buttonSize.div(2),
- text = stringResource(R.string.glanceable_hub_to_dream_button_tooltip),
- )
- }
-
- GoToDreamButton(
- modifier = Modifier.width(buttonSize).height(buttonSize).align(Alignment.End)
- ) {
- viewModel.onShowDreamButtonTap()
- }
- }
- } else {
- GoToDreamButton(modifier = Modifier.width(buttonSize).height(buttonSize)) {
- viewModel.onShowDreamButtonTap()
- }
- }
- }
-
- private fun MutableTransitionState<Boolean>.isCurrentlyVisible() = currentState && isIdle
-
- companion object {
- private val tooltipMaxWidth = 350.dp
- }
-}
-
-@Composable
-private fun GoToDreamButton(modifier: Modifier, onClick: () -> Unit) {
- PlatformIconButton(
- modifier = modifier,
- onClick = onClick,
- iconResource = R.drawable.ic_screensaver_auto,
- contentDescription = stringResource(R.string.accessibility_glanceable_hub_to_dream_button),
- colors =
- IconButtonDefaults.filledIconButtonColors(
- contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
- containerColor = MaterialTheme.colorScheme.primaryContainer,
- ),
- )
-}
-
-@Composable
-private fun Tooltip(pointerOffsetDp: Dp, text: String) {
- Surface(
- color = MaterialTheme.colorScheme.surface,
- shape = TooltipShape(pointerSizeDp = 12.dp, pointerOffsetDp = pointerOffsetDp),
- ) {
- Text(
- modifier = Modifier.padding(start = 32.dp, top = 16.dp, end = 32.dp, bottom = 32.dp),
- color = MaterialTheme.colorScheme.onSurface,
- text = text,
- )
- }
-
- Spacer(modifier = Modifier.height(4.dp))
-}
-
-private class TooltipShape(private val pointerSizeDp: Dp, private val pointerOffsetDp: Dp) : Shape {
-
- override fun createOutline(
- size: Size,
- layoutDirection: LayoutDirection,
- density: Density,
- ): Outline {
-
- val pointerSizePx = with(density) { pointerSizeDp.toPx() }
- val pointerOffsetPx = with(density) { pointerOffsetDp.toPx() }
- val cornerRadius = CornerRadius(CornerSize(16.dp).toPx(size, density))
- val bubbleSize = size.copy(height = size.height - pointerSizePx)
-
- val path =
- Path().apply {
- addRoundRect(
- RoundRect(
- rect = bubbleSize.toRect(),
- topLeft = cornerRadius,
- topRight = cornerRadius,
- bottomRight = cornerRadius,
- bottomLeft = cornerRadius,
- )
- )
- addPath(
- Path().apply {
- moveTo(0f, 0f)
- lineTo(pointerSizePx / 2f, pointerSizePx)
- lineTo(pointerSizePx, 0f)
- close()
- },
- offset =
- Offset(
- x = bubbleSize.width - pointerOffsetPx - pointerSizePx / 2f,
- y = bubbleSize.height,
- ),
- )
- }
-
- return Outline.Generic(path)
- }
-}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
index f905527..b9200c1 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
@@ -55,7 +55,6 @@
val layerCfg =
LayerConfig(
style = FontTextStyle(lineHeight = 147.25f),
- timespec = DigitalTimespec.DIGIT_PAIR,
alignment = DigitalAlignment(HorizontalAlignment.CENTER, VerticalAlignment.CENTER),
aodStyle =
FontTextStyle(
@@ -63,12 +62,23 @@
transitionDuration = 750,
),
- // Placeholder
+ // Placeholders
+ timespec = DigitalTimespec.TIME_FULL_FORMAT,
dateTimeFormat = "hh:mm",
)
- createController(layerCfg.copy(dateTimeFormat = "hh"))
- createController(layerCfg.copy(dateTimeFormat = "mm"))
+ createController(
+ layerCfg.copy(timespec = DigitalTimespec.FIRST_DIGIT, dateTimeFormat = "hh")
+ )
+ createController(
+ layerCfg.copy(timespec = DigitalTimespec.SECOND_DIGIT, dateTimeFormat = "hh")
+ )
+ createController(
+ layerCfg.copy(timespec = DigitalTimespec.FIRST_DIGIT, dateTimeFormat = "mm")
+ )
+ createController(
+ layerCfg.copy(timespec = DigitalTimespec.SECOND_DIGIT, dateTimeFormat = "mm")
+ )
}
private fun refreshTime() {
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
index ec99af1..2d0ca53 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
@@ -124,8 +124,10 @@
maxChildSize.y * AOD_VERTICAL_TRANSLATE_RATIO
)
*/
+
+ val xScale = if (childViews.size < 4) 1f else 2f
val yBuffer = context.resources.getDimensionPixelSize(R.dimen.clock_vertical_digit_buffer)
- return (maxChildSize + aodTranslate.abs()) * VPointF(1f, 2f) + VPointF(0f, yBuffer)
+ return (maxChildSize + aodTranslate.abs()) * VPointF(xScale, 2f) + VPointF(0f, yBuffer)
}
override fun onViewAdded(child: View?) {
@@ -226,19 +228,22 @@
when (child.id) {
R.id.HOUR_FIRST_DIGIT -> VPointF.ZERO
R.id.HOUR_SECOND_DIGIT -> VPointF(x, 0f)
- R.id.MINUTE_FIRST_DIGIT -> VPointF(0f, y + yBuffer)
- R.id.MINUTE_SECOND_DIGIT -> this
R.id.HOUR_DIGIT_PAIR -> VPointF.ZERO
- // Add a small vertical buffer for the second digit pair
+ // Add a small vertical buffer for second line views
R.id.MINUTE_DIGIT_PAIR -> VPointF(0f, y + yBuffer)
+ R.id.MINUTE_FIRST_DIGIT -> VPointF(0f, y + yBuffer)
+ R.id.MINUTE_SECOND_DIGIT -> VPointF(x, y + yBuffer)
else -> VPointF.ZERO
}
}
val childSize = child.measuredSize
- offset += VPointF((measuredWidth - childSize.x) / 2f, 0f)
offset += aodTranslate.abs()
+ // Horizontal offset to center each view in the available space
+ val midX = if (childViews.size < 4) measuredWidth / 2f else measuredWidth / 4f
+ offset += VPointF(midX - childSize.x / 2f, 0f)
+
val setPos = if (isLayout) child::layout else child::setLeftTopRightBottom
setPos(
offset.x.roundToInt(),
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
index 479e146..015a827 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
@@ -425,12 +425,10 @@
return VPointF(
when {
mode.x == EXACTLY -> MeasureSpec.getSize(widthMeasureSpec).toFloat()
- isSingleDigit() -> maxSingleDigitWidth
else -> interpBounds.width() + 2 * lockScreenPaint.strokeWidth
},
when {
mode.y == EXACTLY -> MeasureSpec.getSize(heightMeasureSpec).toFloat()
- isSingleDigit() -> maxSingleDigitHeight
else -> interpBounds.height() + 2 * lockScreenPaint.strokeWidth
},
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 88c9e74..0cfb36d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -36,6 +36,7 @@
import android.view.View
import android.view.WindowInsets
import android.view.WindowManager
+import android.view.accessibility.AccessibilityManager
import android.widget.ScrollView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -114,6 +115,7 @@
@Mock lateinit var selectedUserInteractor: SelectedUserInteractor
@Mock private lateinit var packageManager: PackageManager
@Mock private lateinit var activityTaskManager: ActivityTaskManager
+ @Mock private lateinit var accessibilityManager: AccessibilityManager
@Mock private lateinit var lazyViewCapture: Lazy<ViewCapture>
private lateinit var displayRepository: FakeDisplayRepository
@@ -678,6 +680,7 @@
udfpsUtils,
iconProvider,
activityTaskManager,
+ accessibilityManager,
),
{ credentialViewModel },
fakeExecutor,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt
deleted file mode 100644
index 2f3073e..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt
+++ /dev/null
@@ -1,173 +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.communal.ui.viewmodel
-
-import android.content.pm.UserInfo
-import android.platform.test.annotations.EnableFlags
-import android.provider.Settings
-import android.service.dream.dreamManager
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.internal.logging.uiEventLoggerFake
-import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.communal.data.repository.fakeCommunalPrefsRepository
-import com.android.systemui.communal.domain.interactor.HubOnboardingInteractorTest.Companion.MAIN_USER
-import com.android.systemui.communal.shared.log.CommunalUiEvent
-import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
-import com.android.systemui.flags.fakeFeatureFlagsClassic
-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.plugins.activityStarter
-import com.android.systemui.settings.fakeUserTracker
-import com.android.systemui.statusbar.policy.batteryController
-import com.android.systemui.statusbar.policy.fake
-import com.android.systemui.testKosmos
-import com.android.systemui.user.data.repository.fakeUserRepository
-import com.android.systemui.util.settings.fakeSettings
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.Mockito.verify
-import org.mockito.kotlin.any
-
-@SmallTest
-@EnableFlags(FLAG_GLANCEABLE_HUB_V2)
-@RunWith(AndroidJUnit4::class)
-class CommunalToDreamButtonViewModelTest : SysuiTestCase() {
- private val kosmos = testKosmos()
- private val testScope = kosmos.testScope
- private val underTest: CommunalToDreamButtonViewModel by lazy {
- kosmos.communalToDreamButtonViewModel
- }
-
- @Before
- fun setUp() {
- kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
- underTest.activateIn(testScope)
- }
-
- @Test
- fun shouldShowDreamButtonOnHub_trueWhenPluggedIn() =
- with(kosmos) {
- runTest {
- batteryController.fake._isPluggedIn = true
- runCurrent()
-
- assertThat(underTest.shouldShowDreamButtonOnHub).isTrue()
- }
- }
-
- @Test
- fun shouldShowDreamButtonOnHub_falseWhenNotPluggedIn() =
- with(kosmos) {
- runTest {
- batteryController.fake._isPluggedIn = false
-
- assertThat(underTest.shouldShowDreamButtonOnHub).isFalse()
- }
- }
-
- @Test
- fun onShowDreamButtonTap_dreamsEnabled_startsDream() =
- with(kosmos) {
- runTest {
- val currentUser = fakeUserRepository.asMainUser()
- kosmos.fakeSettings.putIntForUser(
- Settings.Secure.SCREENSAVER_ENABLED,
- 1,
- currentUser.id,
- )
- runCurrent()
-
- underTest.onShowDreamButtonTap()
- runCurrent()
-
- verify(dreamManager).startDream()
- }
- }
-
- @Test
- fun onShowDreamButtonTap_dreamsDisabled_startsActivity() =
- with(kosmos) {
- runTest {
- val currentUser = fakeUserRepository.asMainUser()
- kosmos.fakeSettings.putIntForUser(
- Settings.Secure.SCREENSAVER_ENABLED,
- 0,
- currentUser.id,
- )
- runCurrent()
-
- underTest.onShowDreamButtonTap()
- runCurrent()
-
- verify(activityStarter).postStartActivityDismissingKeyguard(any(), anyInt())
- }
- }
-
- @Test
- fun shouldShowDreamButtonTooltip_trueWhenNotDismissedAndHubOnboardingDismissed() =
- kosmos.runTest {
- setSelectedUser(MAIN_USER)
- fakeCommunalPrefsRepository.setHubOnboardingDismissed(MAIN_USER)
- runCurrent()
-
- assertThat(underTest.shouldShowTooltip).isTrue()
- }
-
- @Test
- fun shouldShowDreamButtonTooltip_falseWhenNotDismissedAndHubOnboardingNotDismissed() =
- kosmos.runTest {
- runCurrent()
- assertThat(underTest.shouldShowTooltip).isFalse()
- }
-
- @Test
- fun shouldShowDreamButtonTooltip_falseWhenDismissed() =
- kosmos.runTest {
- setSelectedUser(MAIN_USER)
- fakeCommunalPrefsRepository.setDreamButtonTooltipDismissed(MAIN_USER)
- runCurrent()
-
- assertThat(underTest.shouldShowTooltip).isFalse()
- }
-
- @Test
- fun onShowDreamButtonTap_eventLogged() =
- with(kosmos) {
- runTest {
- underTest.onShowDreamButtonTap()
- runCurrent()
-
- assertThat(uiEventLoggerFake[0].eventId)
- .isEqualTo(CommunalUiEvent.COMMUNAL_HUB_SHOW_DREAM_BUTTON_TAP.id)
- }
- }
-
- private suspend fun setSelectedUser(user: UserInfo) {
- with(kosmos.fakeUserRepository) {
- setUserInfos(listOf(user))
- setSelectedUserInfo(user)
- }
- kosmos.fakeUserTracker.set(userInfos = listOf(user), selectedUserIndex = 0)
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
index dfea784..197b0ee 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
@@ -23,15 +23,16 @@
import android.view.Display.DEFAULT_DISPLAY
import android.view.Display.TYPE_EXTERNAL
import android.view.Display.TYPE_INTERNAL
+import android.view.IWindowManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.FlowValue
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.util.mockito.kotlinArgumentCaptor
import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
import com.android.systemui.utils.os.FakeHandler
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.TestScope
@@ -46,6 +47,7 @@
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.kotlin.eq
+import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
@@ -53,7 +55,11 @@
class DisplayRepositoryTest : SysuiTestCase() {
private val displayManager = mock<DisplayManager>()
+ private val commandQueue = mock<CommandQueue>()
+ private val windowManager = mock<IWindowManager>()
+
private val displayListener = kotlinArgumentCaptor<DisplayManager.DisplayListener>()
+ private val commandQueueCallbacks = kotlinArgumentCaptor<CommandQueue.Callbacks>()
private val connectedDisplayListener = kotlinArgumentCaptor<DisplayManager.DisplayListener>()
private val testHandler = FakeHandler(Looper.getMainLooper())
@@ -67,6 +73,8 @@
private val displayRepository: DisplayRepositoryImpl by lazy {
DisplayRepositoryImpl(
displayManager,
+ commandQueue,
+ windowManager,
testHandler,
TestScope(UnconfinedTestDispatcher()),
UnconfinedTestDispatcher(),
@@ -513,6 +521,115 @@
assertThat(displayRepository.getDisplay(2)).isNull()
}
+ @Test
+ fun displayIdsWithSystemDecorations_onStart_emitsDisplaysWithSystemDecorations() =
+ testScope.runTest {
+ setDisplays(0, 1, 2)
+ whenever(windowManager.shouldShowSystemDecors(0)).thenReturn(true)
+ whenever(windowManager.shouldShowSystemDecors(1)).thenReturn(false)
+ whenever(windowManager.shouldShowSystemDecors(2)).thenReturn(true)
+
+ val displayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue()
+
+ assertThat(displayIdsWithSystemDecorations).containsExactly(0, 2)
+ }
+
+ @Test
+ fun displayIdsWithSystemDecorations_systemDecorationAdded_emitsIncludingNewDisplayIds() =
+ testScope.runTest {
+ setDisplays(0)
+ whenever(windowManager.shouldShowSystemDecors(0)).thenReturn(true)
+ val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue()
+
+ sendOnDisplayAddSystemDecorations(2)
+ sendOnDisplayAddSystemDecorations(3)
+
+ assertThat(lastDisplayIdsWithSystemDecorations).containsExactly(0, 2, 3)
+ }
+
+ @Test
+ fun displayIdsWithSystemDecorations_systemDecorationAdded_emitsToNewSubscribers() =
+ testScope.runTest {
+ setDisplays(0)
+ whenever(windowManager.shouldShowSystemDecors(0)).thenReturn(true)
+
+ val priorDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue()
+ sendOnDisplayAddSystemDecorations(1)
+ assertThat(priorDisplayIdsWithSystemDecorations).containsExactly(0, 1)
+
+ val lastDisplayIdsWithSystemDecorations by
+ collectLastValue(displayRepository.displayIdsWithSystemDecorations)
+ assertThat(lastDisplayIdsWithSystemDecorations).containsExactly(0, 1)
+ }
+
+ @Test
+ fun displayIdsWithSystemDecorations_systemDecorationRemoved_doesNotEmitRemovedDisplayId() =
+ testScope.runTest {
+ val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue()
+
+ sendOnDisplayAddSystemDecorations(1)
+ sendOnDisplayAddSystemDecorations(2)
+ sendOnDisplayRemoveSystemDecorations(2)
+
+ assertThat(lastDisplayIdsWithSystemDecorations).containsExactly(1)
+ }
+
+ @Test
+ fun displayIdsWithSystemDecorations_systemDecorationsRemoved_nonExistentDisplay_noEffect() =
+ testScope.runTest {
+ val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue()
+
+ sendOnDisplayAddSystemDecorations(1)
+ sendOnDisplayRemoveSystemDecorations(2)
+
+ assertThat(lastDisplayIdsWithSystemDecorations).containsExactly(1)
+ }
+
+ @Test
+ fun displayIdsWithSystemDecorations_displayRemoved_doesNotEmitRemovedDisplayId() =
+ testScope.runTest {
+ val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue()
+
+ sendOnDisplayAddSystemDecorations(1)
+ sendOnDisplayAddSystemDecorations(2)
+ sendOnDisplayRemoved(2)
+
+ assertThat(lastDisplayIdsWithSystemDecorations).containsExactly(1)
+ }
+
+ @Test
+ fun displayIdsWithSystemDecorations_displayRemoved_nonExistentDisplay_noEffect() =
+ testScope.runTest {
+ val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue()
+
+ sendOnDisplayAddSystemDecorations(1)
+ sendOnDisplayRemoved(2)
+
+ assertThat(lastDisplayIdsWithSystemDecorations).containsExactly(1)
+ }
+
+ @Test
+ fun displayIdsWithSystemDecorations_onFlowCollection_commandQueueCallbackRegistered() =
+ testScope.runTest {
+ val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue()
+
+ assertThat(lastDisplayIdsWithSystemDecorations).isEmpty()
+
+ verify(commandQueue, times(1)).addCallback(any())
+ }
+
+ @Test
+ fun displayIdsWithSystemDecorations_afterFlowCollection_commandQueueCallbackUnregistered() {
+ testScope.runTest {
+ val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue()
+
+ assertThat(lastDisplayIdsWithSystemDecorations).isEmpty()
+
+ verify(commandQueue, times(1)).addCallback(any())
+ }
+ verify(commandQueue, times(1)).removeCallback(any())
+ }
+
private fun Iterable<Display>.ids(): List<Int> = map { it.displayId }
private fun Iterable<Set<Display>>.toIdSets(): List<Set<Int>> = map { it.ids().toSet() }
@@ -550,6 +667,14 @@
return flowValue
}
+ // Wrapper to capture the displayListener and commandQueueCallbacks.
+ private fun TestScope.latestDisplayIdsWithSystemDecorationsValue(): FlowValue<Set<Int>?> {
+ val flowValue = collectLastValue(displayRepository.displayIdsWithSystemDecorations)
+ captureAddedRemovedListener()
+ captureCommandQueueCallbacks()
+ return flowValue
+ }
+
private fun captureAddedRemovedListener() {
verify(displayManager)
.registerDisplayListener(
@@ -563,6 +688,10 @@
)
}
+ private fun captureCommandQueueCallbacks() {
+ verify(commandQueue).addCallback(commandQueueCallbacks.capture())
+ }
+
private fun sendOnDisplayAdded(id: Int, displayType: Int) {
val mockDisplay = display(id = id, type = displayType)
whenever(displayManager.getDisplay(eq(id))).thenReturn(mockDisplay)
@@ -592,6 +721,14 @@
connectedDisplayListener.value.onDisplayChanged(id)
}
+ private fun sendOnDisplayRemoveSystemDecorations(id: Int) {
+ commandQueueCallbacks.value.onDisplayRemoveSystemDecorations(id)
+ }
+
+ private fun sendOnDisplayAddSystemDecorations(id: Int) {
+ commandQueueCallbacks.value.onDisplayAddSystemDecorations(id)
+ }
+
private fun setDisplays(displays: List<Display>) {
whenever(displayManager.displays).thenReturn(displays.toTypedArray())
displays.forEach { display ->
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
index 8058eca..1665895 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
@@ -130,7 +130,7 @@
}
@Test
- @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+ @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
fun testApplyDefaultConstraints_LargeClock_SplitShade() =
kosmos.testScope.runTest {
with(kosmos) {
@@ -147,7 +147,7 @@
}
@Test
- @EnableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+ @EnableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
fun testApplyDefaultConstraints_LargeClock_SplitShade_ReactiveVariantsOn() =
kosmos.testScope.runTest {
with(kosmos) {
@@ -165,7 +165,7 @@
}
@Test
- @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+ @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
fun testApplyDefaultConstraints_LargeClock_NonSplitShade() =
kosmos.testScope.runTest {
with(kosmos) {
@@ -189,7 +189,7 @@
}
@Test
- @EnableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+ @EnableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
fun testApplyDefaultConstraints_LargeClock_NonSplitShade_reactiveVariantsOn() =
kosmos.testScope.runTest {
with(kosmos) {
@@ -262,7 +262,7 @@
}
@Test
- @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+ @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
fun testApplyDefaultConstraints_SmallClock_SplitShade() =
kosmos.testScope.runTest {
with(kosmos) {
@@ -286,7 +286,7 @@
}
@Test
- @EnableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+ @EnableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
fun testApplyDefaultConstraints_SmallClock_SplitShade_ReactiveVariantsOn() =
kosmos.testScope.runTest {
with(kosmos) {
@@ -311,7 +311,7 @@
}
@Test
- @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+ @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
fun testApplyDefaultConstraints_SmallClock_NonSplitShade() =
kosmos.testScope.runTest {
with(kosmos) {
@@ -334,7 +334,7 @@
}
@Test
- @EnableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+ @EnableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
fun testApplyDefaultConstraints_SmallClock_NonSplitShade_ReactiveVariantsOn() =
kosmos.testScope.runTest {
with(kosmos) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
index f9bed23..374bcbf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
@@ -50,7 +50,7 @@
@RunWith(AndroidJUnit4::class)
@SmallTest
-@DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+@DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
class SmartspaceSectionTest : SysuiTestCase() {
private lateinit var underTest: SmartspaceSection
@Mock private lateinit var keyguardClockViewModel: KeyguardClockViewModel
@@ -102,7 +102,7 @@
}
@Test
- @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+ @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
fun testAddViews_notSmartspaceEnabled() {
whenever(keyguardSmartspaceViewModel.isSmartspaceEnabled).thenReturn(false)
val constraintLayout = ConstraintLayout(mContext)
@@ -113,7 +113,7 @@
}
@Test
- @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+ @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
fun testAddViews_smartspaceEnabled_dateWeatherDecoupled() {
whenever(keyguardSmartspaceViewModel.isDateWeatherDecoupled).thenReturn(true)
underTest.addViews(constraintLayout)
@@ -132,7 +132,7 @@
}
@Test
- @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+ @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
fun testConstraintsWhenShadeLayoutIsNotWide() {
underTest.addViews(constraintLayout)
underTest.applyConstraints(constraintSet)
@@ -142,7 +142,7 @@
}
@Test
- @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+ @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
fun testConstraintsWhenShadeLayoutIsWide() {
isShadeLayoutWide.value = true
@@ -154,7 +154,7 @@
}
@Test
- @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+ @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
fun testConstraintsWhenNotHasCustomWeatherDataDisplay() {
whenever(keyguardSmartspaceViewModel.isDateWeatherDecoupled).thenReturn(true)
underTest.addViews(constraintLayout)
@@ -169,7 +169,7 @@
}
@Test
- @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+ @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
fun testConstraintsWhenHasCustomWeatherDataDisplay() {
hasCustomWeatherDataDisplay.value = true
underTest.addViews(constraintLayout)
@@ -180,7 +180,7 @@
}
@Test
- @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+ @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
fun testNormalDateWeatherVisibility() {
isWeatherVisibleFlow.value = true
underTest.addViews(constraintLayout)
@@ -194,7 +194,7 @@
}
@Test
- @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_VARIANTS)
+ @DisableFlags(com.android.systemui.shared.Flags.FLAG_CLOCK_REACTIVE_SMARTSPACE_LAYOUT)
fun testCustomDateWeatherVisibility() {
hasCustomWeatherDataDisplay.value = true
underTest.addViews(constraintLayout)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt
index 4e14fec..c71b107 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt
@@ -61,11 +61,11 @@
fun setUp() {
context.orCreateTestableResources.addOverride(
R.dimen.qs_media_enabled_seekbar_height,
- enabledHeight
+ enabledHeight,
)
context.orCreateTestableResources.addOverride(
R.dimen.qs_media_disabled_seekbar_height,
- disabledHeight
+ disabledHeight,
)
seekBarView = SeekBar(context)
@@ -116,9 +116,6 @@
// THEN seek bar shows the progress
assertThat(seekBarView.progress).isEqualTo(3000)
assertThat(seekBarView.max).isEqualTo(120000)
-
- val desc = context.getString(R.string.controls_media_seekbar_description, "00:03", "02:00")
- assertThat(seekBarView.contentDescription).isEqualTo(desc)
}
@Test
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
index 67d0ade..0caddf4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/ConversationNotificationProcessorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/ConversationNotificationProcessorTest.kt
@@ -111,13 +111,13 @@
.isNotNull()
val processedSummary = nb.build().extras.getCharSequence(EXTRA_SUMMARIZED_CONTENT)
- assertThat(processedSummary.toString()).isEqualTo("x$summarization")
+ assertThat(processedSummary.toString()).isEqualTo("x $summarization")
val checkSpans = SpannableStringBuilder(processedSummary)
assertThat(
checkSpans.getSpans(
/* queryStart = */ 0,
- /* queryEnd = */ 1,
+ /* queryEnd = */ 2,
/* kind = */ ImageSpan::class.java,
)
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/PhysicsPropertyAnimatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/PhysicsPropertyAnimatorTest.kt
index 56cd72e..0bb4737 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/PhysicsPropertyAnimatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/PhysicsPropertyAnimatorTest.kt
@@ -15,19 +15,22 @@
*/
package com.android.systemui.statusbar.notification
+import android.animation.AnimatorTestRule
import android.util.FloatProperty
import android.util.Property
import android.view.View
-import androidx.dynamicanimation.animation.DynamicAnimation
+
import androidx.test.annotation.UiThreadTest
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.dynamicanimation.animation.DynamicAnimation
import com.android.systemui.SysuiTestCase
import com.android.systemui.res.R
import com.android.systemui.statusbar.notification.stack.AnimationProperties
import com.android.systemui.statusbar.notification.stack.ViewState
import org.junit.Assert
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito
@@ -50,6 +53,8 @@
return _value
}
}
+ @get:Rule
+ val animatorTestRule = AnimatorTestRule(this)
private val property: PhysicsProperty =
PhysicsProperty(R.id.scale_x_animator_tag, effectiveProperty)
private var finishListener: DynamicAnimation.OnAnimationEndListener? = null
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryTest.kt
index 5d8b68e..83fd5dc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryTest.kt
@@ -105,4 +105,52 @@
fun getWhen_adapter() {
assertThat(underTest.entryAdapter.`when`).isEqualTo(0)
}
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun isColorized() {
+ assertThat(underTest.entryAdapter.isColorized).isFalse()
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun getSbn() {
+ assertThat(underTest.entryAdapter.sbn).isNull()
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun canDragAndDrop() {
+ assertThat(underTest.entryAdapter.canDragAndDrop()).isFalse()
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun isBubble() {
+ assertThat(underTest.entryAdapter.isBubbleCapable).isFalse()
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun getStyle() {
+ assertThat(underTest.entryAdapter.style).isNull()
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun getSectionBucket() {
+ assertThat(underTest.entryAdapter.sectionBucket).isEqualTo(underTest.bucket)
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun isAmbient() {
+ assertThat(underTest.entryAdapter.isAmbient).isFalse()
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun canShowFullScreen() {
+ assertThat(underTest.entryAdapter.isFullScreenCapable()).isFalse()
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
index 34dff24..4810813 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
@@ -21,12 +21,15 @@
import static android.app.Notification.CATEGORY_EVENT;
import static android.app.Notification.CATEGORY_MESSAGE;
import static android.app.Notification.CATEGORY_REMINDER;
+import static android.app.Notification.FLAG_BUBBLE;
import static android.app.Notification.FLAG_FSI_REQUESTED_BUT_DENIED;
import static android.app.Notification.FLAG_PROMOTED_ONGOING;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
import static com.android.systemui.statusbar.NotificationEntryHelper.modifySbn;
+import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_ALERTING;
import static com.google.common.truth.Truth.assertThat;
@@ -35,6 +38,7 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.Notification;
@@ -664,6 +668,126 @@
assertThat(entry.getEntryAdapter().getIcons()).isEqualTo(entry.getIcons());
}
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ public void isColorized() {
+ Notification notification = new Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .setColorized(true)
+ .build();
+
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setNotification(notification)
+ .build();
+ assertThat(entry.getEntryAdapter().isColorized()).isEqualTo(
+ entry.getSbn().getNotification().isColorized());
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ public void getSbn() {
+ Notification notification = new Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .build();
+
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setNotification(notification)
+ .build();
+ assertThat(entry.getEntryAdapter().getSbn()).isEqualTo(
+ entry.getSbn());
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ public void canDragAndDrop() {
+ PendingIntent pi = mock(PendingIntent.class);
+ when(pi.isActivity()).thenReturn(true);
+ Notification notification = new Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .setContentIntent(pi)
+ .build();
+
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setNotification(notification)
+ .build();
+ assertThat(entry.getEntryAdapter().canDragAndDrop()).isTrue();
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ public void isBubble() {
+ Notification notification = new Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .setFlag(FLAG_BUBBLE, true)
+ .build();
+
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setNotification(notification)
+ .build();
+ assertThat(entry.getEntryAdapter().isBubbleCapable()).isEqualTo(entry.isBubble());
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ public void getStyle() {
+ Notification notification = new Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .setStyle(new Notification.BigTextStyle())
+ .build();
+
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setNotification(notification)
+ .build();
+ assertThat(entry.getEntryAdapter().getStyle()).isEqualTo(entry.getNotificationStyle());
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ public void getSectionBucket() {
+ Notification notification = new Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .setStyle(new Notification.BigTextStyle())
+ .build();
+
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setNotification(notification)
+ .build();
+ entry.setBucket(BUCKET_ALERTING);
+
+ assertThat(entry.getEntryAdapter().getSectionBucket()).isEqualTo(BUCKET_ALERTING);
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ public void isAmbient() {
+ Notification notification = new Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .build();
+
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setNotification(notification)
+ .setImportance(IMPORTANCE_MIN)
+ .build();
+
+ assertThat(entry.getEntryAdapter().isAmbient()).isTrue();
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ public void canShowFullScreen() {
+ Notification notification = new Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .setFullScreenIntent(mock(PendingIntent.class), true)
+ .build();
+
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setNotification(notification)
+ .setImportance(IMPORTANCE_MIN)
+ .build();
+
+ assertThat(entry.getEntryAdapter().isFullScreenCapable()).isTrue();
+ }
+
private Notification.Action createContextualAction(String title) {
return new Notification.Action.Builder(
Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
index 2853264..fa185bc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
@@ -18,6 +18,7 @@
import android.app.Flags
import android.app.NotificationChannel
+import android.app.NotificationChannel.SYSTEM_RESERVED_IDS
import android.app.NotificationManager.IMPORTANCE_DEFAULT
import android.app.NotificationManager.IMPORTANCE_HIGH
import android.app.NotificationManager.IMPORTANCE_LOW
@@ -124,6 +125,14 @@
}
@Test
+ fun testPrioritySectioner_doesNotClaim_classifiedConversation() {
+ val sectioner = coordinator.priorityPeopleSectioner
+ for (id in SYSTEM_RESERVED_IDS) {
+ assertFalse(sectioner.isInSection(makeClassifiedConversation(id)))
+ }
+ }
+
+ @Test
fun testPromotesImportantConversations() {
assertTrue(promoter.shouldPromoteToTopLevel(makeEntryOfPeopleType(TYPE_IMPORTANT_PERSON)))
assertFalse(promoter.shouldPromoteToTopLevel(makeEntryOfPeopleType(TYPE_FULL_PERSON)))
@@ -166,6 +175,14 @@
}
@Test
+ fun testAlertingSectioner_doesNotClaim_classifiedConversation() {
+ val sectioner = coordinator.peopleAlertingSectioner
+ for (id in SYSTEM_RESERVED_IDS) {
+ assertFalse(sectioner.isInSection(makeClassifiedConversation(id)))
+ }
+ }
+
+ @Test
fun testInAlertingPeopleSectionWhenTheImportanceIsAtLeastDefault() {
// GIVEN
val alertingEntry = makeEntryOfPeopleType(TYPE_PERSON) { setImportance(IMPORTANCE_DEFAULT) }
@@ -186,6 +203,15 @@
@Test
@DisableFlags(Flags.FLAG_SORT_SECTION_BY_TIME)
+ fun testSilentSectioner_doesNotClaim_classifiedConversation() {
+ val sectioner = coordinator.peopleSilentSectioner
+ for (id in SYSTEM_RESERVED_IDS) {
+ assertFalse(sectioner.isInSection(makeClassifiedConversation(id)))
+ }
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_SORT_SECTION_BY_TIME)
fun testInSilentPeopleSectionWhenTheImportanceIsLowerThanDefault() {
// GIVEN
val silentEntry = makeEntryOfPeopleType(TYPE_PERSON) { setImportance(IMPORTANCE_LOW) }
@@ -280,4 +306,17 @@
assertEquals(type, peopleNotificationIdentifier.getPeopleNotificationType(entry))
return entry
}
+
+ private fun makeClassifiedConversation(channelId: String): NotificationEntry {
+ val channel = NotificationChannel(channelId, channelId, IMPORTANCE_LOW)
+ val entry =
+ NotificationEntryBuilder()
+ .updateRanking {
+ it.setIsConversation(true)
+ it.setShortcutInfo(mock())
+ it.setChannel(channel)
+ }
+ .build()
+ return entry
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt
index d5b2d3a..4e13dcd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt
@@ -20,7 +20,6 @@
import android.media.session.MediaSession
import android.platform.test.flag.junit.FlagsParameterization
import android.provider.Settings
-import android.service.notification.StatusBarNotification
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.dumpManager
@@ -222,16 +221,19 @@
mock<ExpandableNotificationRow>().apply {
whenever(isMediaRow).thenReturn(true)
}
- sbn = SbnBuilder().setNotification(
- Notification.Builder(context, "channel").setStyle(
- MediaStyle().setMediaSession(
- MediaSession(
- context,
- "tag"
- ).sessionToken
+ sbn =
+ SbnBuilder()
+ .setNotification(
+ Notification.Builder(context, "channel")
+ .setStyle(
+ MediaStyle()
+ .setMediaSession(
+ MediaSession(context, "tag").sessionToken
+ )
+ )
+ .build()
)
- ).build()
- ).build()
+ .build()
}
collectionListener.onEntryAdded(fakeEntry)
@@ -631,6 +633,132 @@
}
}
+ @Test
+ fun seenNotificationOnKeyguardMarkedAsSeenIfUpdatedBySystemServer() {
+ // GIVEN: Keyguard is showing, not dozing, unseen notification is present
+ keyguardRepository.setKeyguardShowing(true)
+ keyguardRepository.setIsDozing(false)
+ runKeyguardCoordinatorTest {
+ val fakeEntry = NotificationEntryBuilder().build()
+ collectionListener.onEntryAdded(fakeEntry)
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ this.testScheduler,
+ )
+ testScheduler.runCurrent()
+
+ // WHEN: five seconds have passed
+ testScheduler.advanceTimeBy(5.seconds)
+ testScheduler.runCurrent()
+
+ // WHEN: Keyguard is no longer showing
+ keyguardRepository.setKeyguardShowing(false)
+ kosmos.setTransition(
+ sceneTransition = Idle(Scenes.Gone),
+ stateTransition = TransitionStep(KeyguardState.LOCKSCREEN, KeyguardState.GONE),
+ )
+
+ // WHEN: the notification is updated by the Server
+ collectionListener.onEntryUpdated(fakeEntry, UpdateSource.SystemServer)
+ testScheduler.runCurrent()
+
+ // WHEN: Keyguard is shown again
+ keyguardRepository.setKeyguardShowing(true)
+ kosmos.setTransition(
+ sceneTransition = Idle(Scenes.Lockscreen),
+ stateTransition = TransitionStep(KeyguardState.GONE, KeyguardState.AOD),
+ )
+
+ // THEN: The notification is still recognized as "seen" and is filtered out.
+ assertThat(unseenFilter.shouldFilterOut(fakeEntry, 0L)).isTrue()
+ }
+ }
+
+ @Test
+ fun seenNotificationOnKeyguardMarkedAsSeenIfUpdatedBySystemUi() {
+ // GIVEN: Keyguard is showing, not dozing, unseen notification is present
+ keyguardRepository.setKeyguardShowing(true)
+ keyguardRepository.setIsDozing(false)
+ runKeyguardCoordinatorTest {
+ val fakeEntry = NotificationEntryBuilder().build()
+ collectionListener.onEntryAdded(fakeEntry)
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ this.testScheduler,
+ )
+ testScheduler.runCurrent()
+
+ // WHEN: five seconds have passed
+ testScheduler.advanceTimeBy(5.seconds)
+ testScheduler.runCurrent()
+
+ // WHEN: Keyguard is no longer showing
+ keyguardRepository.setKeyguardShowing(false)
+ kosmos.setTransition(
+ sceneTransition = Idle(Scenes.Gone),
+ stateTransition = TransitionStep(KeyguardState.LOCKSCREEN, KeyguardState.GONE),
+ )
+
+ // WHEN: the notification is updated by the SystemUi
+ collectionListener.onEntryUpdated(fakeEntry, UpdateSource.SystemUi)
+ testScheduler.runCurrent()
+
+ // WHEN: Keyguard is shown again
+ keyguardRepository.setKeyguardShowing(true)
+ kosmos.setTransition(
+ sceneTransition = Idle(Scenes.Lockscreen),
+ stateTransition = TransitionStep(KeyguardState.GONE, KeyguardState.AOD),
+ )
+
+ // THEN: The notification is still recognized as "seen" and is filtered out.
+ assertThat(unseenFilter.shouldFilterOut(fakeEntry, 0L)).isTrue()
+ }
+ }
+
+ @Test
+ fun seenNotificationOnKeyguardMarkedAsUnseenIfUpdatedByApp() {
+ // GIVEN: Keyguard is showing, not dozing, unseen notification is present
+ keyguardRepository.setKeyguardShowing(true)
+ keyguardRepository.setIsDozing(false)
+ runKeyguardCoordinatorTest {
+ val fakeEntry = NotificationEntryBuilder().build()
+ collectionListener.onEntryAdded(fakeEntry)
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ this.testScheduler,
+ )
+ testScheduler.runCurrent()
+
+ // WHEN: five seconds have passed
+ testScheduler.advanceTimeBy(5.seconds)
+ testScheduler.runCurrent()
+
+ // WHEN: Keyguard is no longer showing
+ keyguardRepository.setKeyguardShowing(false)
+ kosmos.setTransition(
+ sceneTransition = Idle(Scenes.Gone),
+ stateTransition = TransitionStep(KeyguardState.LOCKSCREEN, KeyguardState.GONE),
+ )
+
+ // WHEN: the notification is updated by the App
+ collectionListener.onEntryUpdated(fakeEntry, UpdateSource.App)
+ testScheduler.runCurrent()
+
+ // WHEN: Keyguard is shown again
+ keyguardRepository.setKeyguardShowing(true)
+ kosmos.setTransition(
+ sceneTransition = Idle(Scenes.Lockscreen),
+ stateTransition = TransitionStep(KeyguardState.GONE, KeyguardState.AOD),
+ )
+
+ // THEN: The notification is now recognized as "unseen" and is not filtered out.
+ assertThat(unseenFilter.shouldFilterOut(fakeEntry, 0L)).isFalse()
+ }
+ }
+
private fun runKeyguardCoordinatorTest(
testBlock: suspend KeyguardCoordinatorTestScope.() -> Unit
) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
deleted file mode 100644
index 2aa405a..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
+++ /dev/null
@@ -1,1001 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.collection.coordinator;
-
-import static android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static junit.framework.Assert.assertFalse;
-
-import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
-
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeFalse;
-import static org.junit.Assume.assumeTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.FlagsParameterization;
-import android.testing.TestableLooper;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.compose.animation.scene.ObservableTransitionState;
-import com.android.systemui.Flags;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.communal.shared.model.CommunalScenes;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.BrokenWithSceneContainer;
-import com.android.systemui.flags.DisableSceneContainer;
-import com.android.systemui.flags.EnableSceneContainer;
-import com.android.systemui.flags.SceneContainerFlagParameterizationKt;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.keyguard.shared.model.KeyguardState;
-import com.android.systemui.keyguard.shared.model.TransitionState;
-import com.android.systemui.keyguard.shared.model.TransitionStep;
-import com.android.systemui.kosmos.KosmosJavaAdapter;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.scene.shared.flag.SceneContainerFlag;
-import com.android.systemui.shade.data.repository.FakeShadeRepository;
-import com.android.systemui.shade.data.repository.ShadeAnimationRepository;
-import com.android.systemui.shade.data.repository.ShadeRepository;
-import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor;
-import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractorLegacyImpl;
-import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
-import com.android.systemui.statusbar.notification.collection.GroupEntry;
-import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder;
-import com.android.systemui.statusbar.notification.collection.NotifPipeline;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
-import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
-import com.android.systemui.statusbar.notification.data.repository.HeadsUpRepository;
-import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.kotlin.JavaAdapter;
-import com.android.systemui.util.time.FakeSystemClock;
-
-import kotlinx.coroutines.flow.MutableStateFlow;
-import kotlinx.coroutines.test.TestScope;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.verification.VerificationMode;
-
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
-import java.util.List;
-import java.util.Set;
-
-@SmallTest
-@RunWith(ParameterizedAndroidJunit4.class)
-@TestableLooper.RunWithLooper
-public class VisualStabilityCoordinatorTest extends SysuiTestCase {
-
- @Parameters(name = "{0}")
- public static List<FlagsParameterization> getParams() {
- return SceneContainerFlagParameterizationKt
- .andSceneContainer(allCombinationsOf(Flags.FLAG_STABILIZE_HEADS_UP_GROUP_V2));
- }
-
- private VisualStabilityCoordinator mCoordinator;
-
- @Mock private DumpManager mDumpManager;
- @Mock private NotifPipeline mNotifPipeline;
- @Mock private WakefulnessLifecycle mWakefulnessLifecycle;
- @Mock private StatusBarStateController mStatusBarStateController;
- @Mock private Pluggable.PluggableListener<NotifStabilityManager> mInvalidateListener;
- @Mock private SeenNotificationsInteractor mSeenNotificationsInteractor;
- @Mock private HeadsUpRepository mHeadsUpRepository;
- @Mock private VisibilityLocationProvider mVisibilityLocationProvider;
- @Mock private VisualStabilityProvider mVisualStabilityProvider;
- @Mock private VisualStabilityCoordinatorLogger mLogger;
- @Mock private KeyguardStateController mKeyguardStateController;
-
- @Captor private ArgumentCaptor<WakefulnessLifecycle.Observer> mWakefulnessObserverCaptor;
- @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mSBStateListenerCaptor;
- @Captor private ArgumentCaptor<NotifStabilityManager> mNotifStabilityManagerCaptor;
-
- private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
- private FakeSystemClock mFakeSystemClock = new FakeSystemClock();
- private FakeExecutor mFakeBackgroundExecutor = new FakeExecutor(mFakeSystemClock);
- private FakeExecutor mFakeMainExecutor = new FakeExecutor(mFakeSystemClock);
- private final TestScope mTestScope = mKosmos.getTestScope();
- private final JavaAdapter mJavaAdapter = new JavaAdapter(mTestScope.getBackgroundScope());
-
- private ShadeAnimationInteractor mShadeAnimationInteractor;
- private ShadeRepository mShadeRepository;
- private WakefulnessLifecycle.Observer mWakefulnessObserver;
- private StatusBarStateController.StateListener mStatusBarStateListener;
- private NotifStabilityManager mNotifStabilityManager;
- private NotificationEntry mEntry;
- private GroupEntry mGroupEntry;
-
- public VisualStabilityCoordinatorTest(FlagsParameterization flags) {
- super();
- mSetFlagsRule.setFlagsParameterization(flags);
- }
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- mShadeRepository = new FakeShadeRepository();
- mShadeAnimationInteractor = new ShadeAnimationInteractorLegacyImpl(
- new ShadeAnimationRepository(), mShadeRepository);
- mCoordinator = new VisualStabilityCoordinator(
- mFakeBackgroundExecutor,
- mFakeMainExecutor,
- mDumpManager,
- mHeadsUpRepository,
- mShadeAnimationInteractor,
- mJavaAdapter,
- mSeenNotificationsInteractor,
- mStatusBarStateController,
- mVisibilityLocationProvider,
- mVisualStabilityProvider,
- mWakefulnessLifecycle,
- mKosmos.getCommunalSceneInteractor(),
- mKosmos.getShadeInteractor(),
- mKosmos.getKeyguardTransitionInteractor(),
- mKeyguardStateController,
- mLogger);
-
- when(mHeadsUpRepository.isTrackingHeadsUp()).thenReturn(MutableStateFlow(false));
- mCoordinator.attach(mNotifPipeline);
- mTestScope.getTestScheduler().runCurrent();
-
- // capture arguments:
- verify(mWakefulnessLifecycle).addObserver(mWakefulnessObserverCaptor.capture());
- mWakefulnessObserver = mWakefulnessObserverCaptor.getValue();
-
- verify(mStatusBarStateController).addCallback(mSBStateListenerCaptor.capture());
- mStatusBarStateListener = mSBStateListenerCaptor.getValue();
-
- verify(mNotifPipeline).setVisualStabilityManager(mNotifStabilityManagerCaptor.capture());
- mNotifStabilityManager = mNotifStabilityManagerCaptor.getValue();
- mNotifStabilityManager.setInvalidationListener(mInvalidateListener);
-
- mEntry = new NotificationEntryBuilder()
- .setPkg("testPkg1")
- .build();
-
- mGroupEntry = new GroupEntryBuilder()
- .setSummary(mEntry)
- .build();
-
- when(mHeadsUpRepository.isHeadsUpEntry(mEntry.getKey())).thenReturn(false);
-
- // Whenever we invalidate, the pipeline runs again, so we invalidate the state
- doAnswer(i -> {
- mNotifStabilityManager.onBeginRun();
- return null;
- }).when(mInvalidateListener).onPluggableInvalidated(eq(mNotifStabilityManager), any());
- }
-
- @Test
- public void testScreenOff_groupAndSectionChangesAllowed() {
- // GIVEN screen is off, panel isn't expanded and device isn't pulsing
- setFullyDozed(true);
- setSleepy(true);
- setPanelExpanded(false);
- setPulsing(false);
-
- // THEN group changes are allowed
- assertTrue(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
- assertTrue(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
-
- // THEN section changes are allowed
- assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
- }
-
- @Test
- public void testScreenTurningOff_groupAndSectionChangesNotAllowed() {
- // GIVEN the screen is turning off (sleepy but partially dozed)
- setFullyDozed(false);
- setSleepy(true);
- setPanelExpanded(true);
- setPulsing(false);
-
- // THEN group changes are NOT allowed
- assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
- assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
-
- // THEN section changes are NOT allowed
- assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
- }
-
- @Test
- public void testScreenTurningOn_groupAndSectionChangesNotAllowed() {
- // GIVEN the screen is turning on (still fully dozed, not sleepy)
- setFullyDozed(true);
- setSleepy(false);
- setPanelExpanded(true);
- setPulsing(false);
-
- // THEN group changes are NOT allowed
- assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
- assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
-
- // THEN section changes are NOT allowed
- assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
- }
-
- @Test
- public void testPanelNotExpanded_groupAndSectionChangesAllowed() {
- // GIVEN screen is on but the panel isn't expanded and device isn't pulsing
- setFullyDozed(false);
- setSleepy(false);
- setPanelExpanded(false);
- setPulsing(false);
-
- // THEN group changes are allowed
- assertTrue(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
- assertTrue(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
-
- // THEN section changes are allowed
- assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
- }
-
- @Test
- public void testPanelExpanded_groupAndSectionChangesNotAllowed() {
- // GIVEN the panel true expanded and device isn't pulsing
- setFullyDozed(false);
- setSleepy(false);
- setPanelExpanded(true);
- setPulsing(false);
-
- // THEN group changes are NOT allowed
- assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
- assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
-
- // THEN section changes are NOT allowed
- assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
- }
-
- @Test
- @BrokenWithSceneContainer(bugId = 377868472) // mReorderingAllowed is broken with SceneContainer
- public void testLockscreenPartlyShowing_groupAndSectionChangesNotAllowed() {
- // GIVEN the panel true expanded and device isn't pulsing
- setFullyDozed(false);
- setSleepy(false);
- setLockscreenShowing(0.5f);
- setPulsing(false);
-
- // THEN group changes are NOT allowed
- assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
- assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
-
- // THEN section changes are NOT allowed
- assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
- }
-
- @Test
- @BrokenWithSceneContainer(bugId = 377868472) // mReorderingAllowed is broken with SceneContainer
- public void testLockscreenFullyShowing_groupAndSectionChangesNotAllowed() {
- // GIVEN the panel true expanded and device isn't pulsing
- setFullyDozed(false);
- setSleepy(false);
- setLockscreenShowing(1.0f);
- setPulsing(false);
-
- // THEN group changes are NOT allowed
- assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
- assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
-
- // THEN section changes are NOT allowed
- assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
- }
-
- @Test
- public void testPulsing_screenOff_groupAndSectionChangesNotAllowed() {
- // GIVEN the device is pulsing and screen is off
- setFullyDozed(true);
- setSleepy(true);
- setPulsing(true);
-
- // THEN group changes are NOT allowed
- assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
- assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
-
- // THEN section changes are NOT allowed
- assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
- }
-
- @Test
- public void testPulsing_panelNotExpanded_groupAndSectionChangesNotAllowed() {
- // GIVEN the device is pulsing and screen is off with the panel not expanded
- setFullyDozed(true);
- setSleepy(true);
- setPanelExpanded(false);
- setPulsing(true);
-
- // THEN group changes are NOT allowed
- assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
- assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
-
- // THEN section changes are NOT allowed
- assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
- }
-
- @Test
- public void testOverrideReorderingSuppression_onlySectionChangesAllowed() {
- // GIVEN section changes typically wouldn't be allowed because the panel is expanded and
- // we're not pulsing
- setFullyDozed(false);
- setSleepy(false);
- setPanelExpanded(true);
- setPulsing(true);
-
- // WHEN we temporarily allow section changes for this notification entry
- mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.currentTimeMillis());
-
- // THEN group changes aren't allowed
- assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
- assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
-
- // THEN section changes are allowed for this notification but not other notifications
- assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
- assertFalse(mNotifStabilityManager.isSectionChangeAllowed(
- new NotificationEntryBuilder()
- .setPkg("testPkg2")
- .build()));
- }
-
- @Test
- public void testTemporarilyAllowSectionChanges_callsInvalidate() {
- // GIVEN section changes typically wouldn't be allowed because the panel is expanded
- setFullyDozed(false);
- setSleepy(false);
- setPanelExpanded(true);
- setPulsing(false);
-
- // WHEN we temporarily allow section changes for this notification entry
- mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.uptimeMillis());
-
- // THEN the notification list is invalidated
- verifyStabilityManagerWasInvalidated(times(1));
- }
-
- @Test
- public void testTemporarilyAllowSectionChanges_noInvalidationCalled() {
- // GIVEN section changes typically WOULD be allowed
- setFullyDozed(true);
- setSleepy(true);
- setPanelExpanded(false);
- setPulsing(false);
-
- // WHEN we temporarily allow section changes for this notification entry
- mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.currentTimeMillis());
-
- // THEN invalidate is not called because this entry was never suppressed from reordering
- verifyStabilityManagerWasInvalidated(never());
- }
-
- @Test
- public void testTemporarilyAllowSectionChangesTimeout() {
- // GIVEN section changes typically WOULD be allowed
- setFullyDozed(true);
- setSleepy(true);
- setPanelExpanded(false);
- setPulsing(false);
- assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
-
- // WHEN we temporarily allow section changes for this notification entry
- mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.currentTimeMillis());
-
- // THEN invalidate is not called because this entry was never suppressed from reordering;
- // THEN section changes are allowed for this notification
- verifyStabilityManagerWasInvalidated(never());
- assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
-
- // WHEN we're pulsing (now disallowing reordering)
- setPulsing(true);
-
- // THEN we're still allowed to reorder this section because it's still in the list of
- // notifications to allow section changes
- assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
-
- // WHEN the timeout for the temporarily allow section reordering runnable is finsihed
- mFakeBackgroundExecutor.advanceClockToNext();
- mFakeBackgroundExecutor.runNextReady();
-
- // THEN section changes aren't allowed anymore
- assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
- }
-
- @Test
- public void testTemporarilyAllowSectionChanges_isPulsingChangeBeforeTimeout() {
- // GIVEN section changes typically wouldn't be allowed because the device is pulsing
- setFullyDozed(true);
- setSleepy(true);
- setPanelExpanded(false);
- setPulsing(true);
-
- // WHEN we temporarily allow section changes for this notification entry
- mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.currentTimeMillis());
- // can now reorder, so invalidates
- verifyStabilityManagerWasInvalidated(times(1));
-
- // WHEN reordering is now allowed because device isn't pulsing anymore
- setPulsing(false);
-
- // THEN invalidate isn't called a second time since reordering was already allowed
- verifyStabilityManagerWasInvalidated(times(1));
- }
-
- @Test
- public void testMovingVisibleHeadsUpNotAllowed() {
- // GIVEN stability enforcing conditions
- setPanelExpanded(true);
- setSleepy(false);
-
- // WHEN a notification is alerting and visible
- when(mHeadsUpRepository.isHeadsUpEntry(mEntry.getKey())).thenReturn(true);
- when(mVisibilityLocationProvider.isInVisibleLocation(any(NotificationEntry.class)))
- .thenReturn(true);
-
- // VERIFY the notification cannot be reordered
- assertThat(mNotifStabilityManager.isEntryReorderingAllowed(mEntry)).isFalse();
- assertThat(mNotifStabilityManager.isSectionChangeAllowed(mEntry)).isFalse();
- }
-
- @Test
- public void testMovingInvisibleHeadsUpAllowed() {
- // GIVEN stability enforcing conditions
- setPanelExpanded(true);
- setSleepy(false);
-
- // WHEN a notification is alerting but not visible
- when(mHeadsUpRepository.isHeadsUpEntry(mEntry.getKey())).thenReturn(true);
- when(mVisibilityLocationProvider.isInVisibleLocation(any(NotificationEntry.class)))
- .thenReturn(false);
-
- // VERIFY the notification can be reordered
- assertThat(mNotifStabilityManager.isEntryReorderingAllowed(mEntry)).isTrue();
- assertThat(mNotifStabilityManager.isSectionChangeAllowed(mEntry)).isTrue();
- }
-
- @Test
- public void testNeverSuppressedChanges_noInvalidationCalled() {
- // GIVEN no notifications are currently being suppressed from grouping nor being sorted
-
- // WHEN device isn't pulsing anymore
- setPulsing(false);
-
- // WHEN fully dozed
- setFullyDozed(true);
-
- // WHEN sleepy
- setSleepy(true);
-
- // WHEN panel isn't expanded
- setPanelExpanded(false);
-
- // THEN we never see any calls to invalidate since there weren't any notifications that
- // were being suppressed from grouping or section changes
- verifyStabilityManagerWasInvalidated(never());
- }
-
- @Test
- public void testNotSuppressingGroupChangesAnymore_invalidationCalled() {
- // GIVEN visual stability is being maintained b/c panel is expanded
- setPulsing(false);
- setFullyDozed(false);
- setSleepy(false);
- setPanelExpanded(true);
-
- assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
- assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
-
- // WHEN the panel isn't expanded anymore
- setPanelExpanded(false);
-
- // invalidate is called because we were previously suppressing a group change
- verifyStabilityManagerWasInvalidated(times(1));
- }
-
- @Test
- public void testNotLaunchingActivityAnymore_invalidationCalled() {
- // GIVEN visual stability is being maintained b/c animation is playing
- setActivityLaunching(true);
-
- assertFalse(mNotifStabilityManager.isPipelineRunAllowed());
-
- // WHEN the animation has stopped playing
- setActivityLaunching(false);
-
- // invalidate is called, b/c we were previously suppressing the pipeline from running
- verifyStabilityManagerWasInvalidated(times(1));
- }
-
- @Test
- public void testNotCollapsingPanelAnymore_invalidationCalled() {
- // GIVEN visual stability is being maintained b/c animation is playing
- setPanelCollapsing(true);
-
- assertFalse(mNotifStabilityManager.isPipelineRunAllowed());
-
- // WHEN the animation has stopped playing
- setPanelCollapsing(false);
-
- // invalidate is called, b/c we were previously suppressing the pipeline from running
- verifyStabilityManagerWasInvalidated(times(1));
- }
-
- @Test
- @EnableFlags(Flags.FLAG_CHECK_LOCKSCREEN_GONE_TRANSITION)
- @DisableSceneContainer
- public void testNotLockscreenInGoneTransitionLegacy_invalidationCalled() {
- // GIVEN visual stability is being maintained b/c animation is playing
- doReturn(true).when(mKeyguardStateController).isKeyguardFadingAway();
- mCoordinator.mKeyguardFadeAwayAnimationCallback.onKeyguardFadingAwayChanged();
-
- assertFalse(mNotifStabilityManager.isPipelineRunAllowed());
-
- // WHEN the animation has stopped playing
- doReturn(false).when(mKeyguardStateController).isKeyguardFadingAway();
- mCoordinator.mKeyguardFadeAwayAnimationCallback.onKeyguardFadingAwayChanged();
-
- // invalidate is called, b/c we were previously suppressing the pipeline from running
- verifyStabilityManagerWasInvalidated(times(1));
- }
-
- @Test
- @EnableFlags(Flags.FLAG_CHECK_LOCKSCREEN_GONE_TRANSITION)
- @EnableSceneContainer
- @BrokenWithSceneContainer(bugId = 377868472) // mReorderingAllowed is broken with SceneContainer
- public void testNotLockscreenInGoneTransition_invalidationCalled() {
- // GIVEN visual stability is being maintained b/c animation is playing
- mKosmos.getKeyguardTransitionRepository().sendTransitionStepJava(
- mTestScope, new TransitionStep(
- KeyguardState.LOCKSCREEN,
- KeyguardState.GONE,
- 1f,
- TransitionState.RUNNING), /* validateStep = */ false);
- mTestScope.getTestScheduler().runCurrent();
- assertFalse(mNotifStabilityManager.isPipelineRunAllowed());
-
- // WHEN the animation has stopped playing
- mKosmos.getKeyguardTransitionRepository().sendTransitionStepJava(
- mTestScope, new TransitionStep(
- KeyguardState.LOCKSCREEN,
- KeyguardState.GONE,
- 1f,
- TransitionState.FINISHED), /* validateStep = */ false);
- mTestScope.getTestScheduler().runCurrent();
-
- // invalidate is called, b/c we were previously suppressing the pipeline from running
- verifyStabilityManagerWasInvalidated(times(1));
- }
-
- @Test
- public void testNeverSuppressPipelineRunFromPanelCollapse_noInvalidationCalled() {
- // GIVEN animation is playing
- setPanelCollapsing(true);
-
- // WHEN the animation has stopped playing
- setPanelCollapsing(false);
-
- // THEN invalidate is not called, b/c nothing has been suppressed
- verifyStabilityManagerWasInvalidated(never());
- }
-
- @Test
- public void testNeverSuppressPipelineRunFromLaunchActivity_noInvalidationCalled() {
- // GIVEN animation is playing
- setActivityLaunching(true);
-
- // WHEN the animation has stopped playing
- setActivityLaunching(false);
-
- // THEN invalidate is not called, b/c nothing has been suppressed
- verifyStabilityManagerWasInvalidated(never());
- }
-
- @Test
- public void testNotSuppressingEntryReorderingAnymoreWillInvalidate() {
- // GIVEN visual stability is being maintained b/c panel is expanded
- setPulsing(false);
- setFullyDozed(false);
- setSleepy(false);
- setPanelExpanded(true);
- setCommunalShowing(false);
-
- assertFalse(mNotifStabilityManager.isEntryReorderingAllowed(mEntry));
- // The pipeline still has to report back that entry reordering was suppressed
- mNotifStabilityManager.onEntryReorderSuppressed();
-
- // WHEN the panel isn't expanded anymore
- setPanelExpanded(false);
-
- // invalidate is called because we were previously suppressing an entry reorder
- verifyStabilityManagerWasInvalidated(times(1));
- }
-
- @Test
- @BrokenWithSceneContainer(bugId = 377868472) // mReorderingAllowed is broken with SceneContainer
- public void testCommunalShowingWillNotSuppressReordering() {
- // GIVEN panel is expanded, communal is showing, and QS is collapsed
- setPulsing(false);
- setFullyDozed(false);
- setSleepy(false);
- setPanelExpanded(true);
- setQsExpanded(false);
- setCommunalShowing(true);
-
- // Reordering should be allowed
- assertTrue(mNotifStabilityManager.isEntryReorderingAllowed(mEntry));
- }
-
- @Test
- public void testQsExpandedOverCommunalWillSuppressReordering() {
- // GIVEN panel is expanded and communal is showing, but QS is expanded
- setPulsing(false);
- setFullyDozed(false);
- setSleepy(false);
- setPanelExpanded(true);
- setQsExpanded(true);
- setCommunalShowing(true);
-
- // Reordering should not be allowed
- assertFalse(mNotifStabilityManager.isEntryReorderingAllowed(mEntry));
- }
-
- @Test
- public void testQueryingEntryReorderingButNotReportingReorderSuppressedDoesNotInvalidate() {
- // GIVEN visual stability is being maintained b/c panel is expanded
- setPulsing(false);
- setFullyDozed(false);
- setSleepy(false);
- setPanelExpanded(true);
-
- assertFalse(mNotifStabilityManager.isEntryReorderingAllowed(mEntry));
-
- // WHEN the panel isn't expanded anymore
- setPanelExpanded(false);
-
- // invalidate is not called because we were not told that an entry reorder was suppressed
- verifyStabilityManagerWasInvalidated(never());
- }
-
- @Test
- public void testHeadsUp_allowedToChangeGroupAndSection() {
- // GIVEN group + section changes disallowed
- setFullyDozed(false);
- setSleepy(false);
- setPanelExpanded(true);
- setPulsing(true);
- assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
- assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
- assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
-
- // GIVEN mEntry is a HUN
- when(mHeadsUpRepository.isHeadsUpEntry(mEntry.getKey())).thenReturn(true);
-
- // THEN group + section changes are allowed
- assertTrue(mNotifStabilityManager.isGroupChangeAllowed(mEntry));
- assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry));
-
- // BUT pruning the group for which this is the summary would still NOT be allowed.
- assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry));
- }
-
- @Test
- public void everyChangeAllowed_onReorderingEnabled_legacy() {
- assumeFalse(StabilizeHeadsUpGroup.isEnabled());
- // GIVEN - reordering is allowed.
- setPulsing(false);
- setPanelExpanded(false);
-
- // THEN
- assertThat(mNotifStabilityManager.isEveryChangeAllowed()).isTrue();
- assertThat(mNotifStabilityManager.isGroupChangeAllowed(any())).isTrue();
- assertThat(mNotifStabilityManager.isGroupPruneAllowed(any())).isTrue();
- assertThat(mNotifStabilityManager.isSectionChangeAllowed(any())).isTrue();
- assertThat(mNotifStabilityManager.isEntryReorderingAllowed(any())).isTrue();
- }
-
- @Test
- public void everyChangeAllowed_noActiveHeadsUpGroup_onReorderingEnabled() {
- assumeTrue(StabilizeHeadsUpGroup.isEnabled());
- // GIVEN - reordering is allowed.
- setPulsing(false);
- setPanelExpanded(false);
-
- // GIVEN - empty heads-up-group keys
- mCoordinator.setHeadsUpGroupKeys(Set.of());
-
- // THEN
- assertThat(mNotifStabilityManager.isEveryChangeAllowed()).isTrue();
- assertThat(mNotifStabilityManager.isGroupChangeAllowed(any())).isTrue();
- assertThat(mNotifStabilityManager.isGroupPruneAllowed(any())).isTrue();
- assertThat(mNotifStabilityManager.isSectionChangeAllowed(any())).isTrue();
- assertThat(mNotifStabilityManager.isEntryReorderingAllowed(any())).isTrue();
- }
-
- @Test
- public void everyChangeDisallowed_activeHeadsUpGroup_onReorderingEnabled() {
- assumeTrue(StabilizeHeadsUpGroup.isEnabled());
- // GIVEN - reordering is allowed.
- setPulsing(false);
- setPanelExpanded(false);
-
- // GIVEN - there is a group heads-up.
- mCoordinator.setHeadsUpGroupKeys(Set.of("heads_up_group_key"));
-
- // THEN
- assertThat(mNotifStabilityManager.isEveryChangeAllowed()).isFalse();
- }
-
- @Test
- public void nonHeadsUpGroup_changesAllowed_onReorderingEnabled() {
- assumeTrue(StabilizeHeadsUpGroup.isEnabled());
- // GIVEN - reordering is allowed.
- setPulsing(false);
- setPanelExpanded(false);
-
- // GIVEN - there is a group heads-up.
- String headsUpGroupKey = "heads_up_group_key";
- mCoordinator.setHeadsUpGroupKeys(Set.of(headsUpGroupKey));
- when(mHeadsUpRepository.isHeadsUpEntry(headsUpGroupKey)).thenReturn(true);
-
- // GIVEN - HUN Group Summary
- final NotificationEntry nonHeadsUpGroupSummary = mock(NotificationEntry.class);
- when(nonHeadsUpGroupSummary.getKey()).thenReturn("non_heads_up_group_key");
- when(nonHeadsUpGroupSummary.isSummaryWithChildren()).thenReturn(true);
- final GroupEntry nonHeadsUpGroupEntry = mock(GroupEntry.class);
- when(nonHeadsUpGroupEntry.getSummary()).thenReturn(nonHeadsUpGroupSummary);
- when(nonHeadsUpGroupEntry.getRepresentativeEntry()).thenReturn(nonHeadsUpGroupSummary);
-
- // THEN
- assertThat(mNotifStabilityManager.isGroupPruneAllowed(nonHeadsUpGroupEntry)).isTrue();
- assertThat(mNotifStabilityManager.isEntryReorderingAllowed(nonHeadsUpGroupEntry)).isTrue();
- }
-
- @Test
- public void headsUpGroup_changesDisallowed_onReorderingEnabled() {
- assumeTrue(StabilizeHeadsUpGroup.isEnabled());
- // GIVEN - reordering is allowed.
- setPulsing(false);
- setPanelExpanded(false);
-
- // GIVEN - there is a group heads-up.
- final String headsUpGroupKey = "heads_up_group_key";
- mCoordinator.setHeadsUpGroupKeys(Set.of(headsUpGroupKey));
- when(mHeadsUpRepository.isHeadsUpEntry(headsUpGroupKey)).thenReturn(true);
-
- // GIVEN - HUN Group
- final NotificationEntry headsUpGroupSummary = mock(NotificationEntry.class);
- when(headsUpGroupSummary.rowIsChildInGroup()).thenReturn(false);
- when(headsUpGroupSummary.getKey()).thenReturn(headsUpGroupKey);
- when(headsUpGroupSummary.isSummaryWithChildren()).thenReturn(true);
-
- final GroupEntry headsUpGroupEntry = mock(GroupEntry.class);
- when(headsUpGroupEntry.getSummary()).thenReturn(headsUpGroupSummary);
- when(headsUpGroupEntry.getRepresentativeEntry()).thenReturn(headsUpGroupSummary);
-
- when(headsUpGroupSummary.getParent()).thenReturn(headsUpGroupEntry);
-
- // GIVEN - HUN is in visible location
- when(mVisibilityLocationProvider.isInVisibleLocation(headsUpGroupSummary)).thenReturn(true);
-
- // THEN
- assertThat(mNotifStabilityManager.isGroupPruneAllowed(headsUpGroupEntry)).isFalse();
- assertThat(mNotifStabilityManager.isEntryReorderingAllowed(headsUpGroupEntry)).isFalse();
- }
-
- @Test
- public void headsUpGroupSummaries_changesDisallowed_onReorderingEnabled() {
- assumeTrue(StabilizeHeadsUpGroup.isEnabled());
- // GIVEN - reordering is allowed.
- setPulsing(false);
- setPanelExpanded(false);
-
- // GIVEN - there is a group heads-up.
- final String headsUpGroupKey = "heads_up_group_key";
- mCoordinator.setHeadsUpGroupKeys(Set.of(headsUpGroupKey));
- when(mHeadsUpRepository.isHeadsUpEntry(headsUpGroupKey)).thenReturn(true);
-
- // GIVEN - HUN Group
- final NotificationEntry headsUpGroupSummary = mock(NotificationEntry.class);
- when(headsUpGroupSummary.rowIsChildInGroup()).thenReturn(false);
- when(headsUpGroupSummary.getKey()).thenReturn(headsUpGroupKey);
- when(headsUpGroupSummary.isSummaryWithChildren()).thenReturn(true);
-
- final GroupEntry headsUpGroupEntry = mock(GroupEntry.class);
- when(headsUpGroupEntry.getSummary()).thenReturn(headsUpGroupSummary);
- when(headsUpGroupEntry.getRepresentativeEntry()).thenReturn(headsUpGroupSummary);
-
- when(headsUpGroupSummary.getParent()).thenReturn(headsUpGroupEntry);
-
- // GIVEN - HUN is in visible location
- when(mVisibilityLocationProvider.isInVisibleLocation(headsUpGroupSummary)).thenReturn(true);
-
- // THEN
- assertThat(mNotifStabilityManager.isGroupChangeAllowed(headsUpGroupSummary)).isFalse();
- assertThat(mNotifStabilityManager.isEntryReorderingAllowed(headsUpGroupSummary)).isFalse();
- assertThat(mNotifStabilityManager.isSectionChangeAllowed(headsUpGroupSummary)).isFalse();
- }
-
- @Test
- public void notificationInNonHUNGroup_changesAllowed_onReorderingEnabled() {
- assumeTrue(StabilizeHeadsUpGroup.isEnabled());
- // GIVEN - reordering is allowed.
- setPulsing(false);
- setPanelExpanded(false);
-
- // GIVEN - there is a group heads-up.
- String headsUpGroupKey = "heads_up_group_key";
- mCoordinator.setHeadsUpGroupKeys(Set.of(headsUpGroupKey));
- when(mHeadsUpRepository.isHeadsUpEntry(headsUpGroupKey)).thenReturn(true);
-
- // GIVEN - non HUN parent Group Summary
- final NotificationEntry groupSummary = mock(NotificationEntry.class);
- when(groupSummary.getKey()).thenReturn("non_heads_up_group_key");
- when(groupSummary.isSummaryWithChildren()).thenReturn(true);
-
- final GroupEntry nonHeadsUpGroupEntry = mock(GroupEntry.class);
- when(nonHeadsUpGroupEntry.getSummary()).thenReturn(groupSummary);
- when(nonHeadsUpGroupEntry.getRepresentativeEntry()).thenReturn(groupSummary);
-
- // GIVEN - child entry in a non heads-up group.
- final NotificationEntry childEntry = mock(NotificationEntry.class);
- when(childEntry.rowIsChildInGroup()).thenReturn(true);
- when(childEntry.getParent()).thenReturn(nonHeadsUpGroupEntry);
- when(childEntry.getParent()).thenReturn(nonHeadsUpGroupEntry);
-
- // THEN
- assertThat(mNotifStabilityManager.isGroupChangeAllowed(childEntry)).isTrue();
- assertThat(mNotifStabilityManager.isSectionChangeAllowed(childEntry)).isTrue();
- assertThat(mNotifStabilityManager.isEntryReorderingAllowed(nonHeadsUpGroupEntry)).isTrue();
- }
-
- @Test
- public void notificationInHUNGroup_changesDisallowed_reorderingEnabled() {
- assumeTrue(StabilizeHeadsUpGroup.isEnabled());
- // GIVEN - reordering is allowed.
- setPulsing(false);
- setPanelExpanded(false);
-
- // GIVEN - there is a group heads-up.
- final String headsUpGroupKey = "heads_up_group_key";
- mCoordinator.setHeadsUpGroupKeys(Set.of(headsUpGroupKey));
- when(mHeadsUpRepository.isHeadsUpEntry(headsUpGroupKey)).thenReturn(true);
-
- // GIVEN - HUN Group Summary
- final NotificationEntry headsUpGroupSummary = mock(NotificationEntry.class);
- when(headsUpGroupSummary.rowIsChildInGroup()).thenReturn(false);
- when(headsUpGroupSummary.getKey()).thenReturn(headsUpGroupKey);
- when(headsUpGroupSummary.isSummaryWithChildren()).thenReturn(true);
-
- final GroupEntry nonHeadsUpGroupEntry = mock(GroupEntry.class);
- when(nonHeadsUpGroupEntry.getSummary()).thenReturn(headsUpGroupSummary);
- when(nonHeadsUpGroupEntry.getRepresentativeEntry()).thenReturn(headsUpGroupSummary);
-
- // GIVEN - child entry in a non heads-up group.
- final NotificationEntry childEntry = mock(NotificationEntry.class);
- when(childEntry.rowIsChildInGroup()).thenReturn(true);
- when(childEntry.getParent()).thenReturn(nonHeadsUpGroupEntry);
-
- // GIVEN - HUN is in visible location
- when(mVisibilityLocationProvider.isInVisibleLocation(headsUpGroupSummary)).thenReturn(true);
-
- // THEN
- assertThat(mNotifStabilityManager.isGroupChangeAllowed(childEntry)).isFalse();
- assertThat(mNotifStabilityManager.isSectionChangeAllowed(childEntry)).isFalse();
- assertThat(mNotifStabilityManager.isEntryReorderingAllowed(childEntry)).isFalse();
- }
-
- private void verifyStabilityManagerWasInvalidated(VerificationMode mode) {
- verify(mInvalidateListener, mode).onPluggableInvalidated(eq(mNotifStabilityManager), any());
- }
-
- private void setActivityLaunching(boolean activityLaunching) {
- mShadeAnimationInteractor.setIsLaunchingActivity(activityLaunching);
- mTestScope.getTestScheduler().runCurrent();
- }
-
- private void setPanelCollapsing(boolean collapsing) {
- mShadeRepository.setLegacyIsClosing(collapsing);
- mTestScope.getTestScheduler().runCurrent();
- }
-
- private void setCommunalShowing(boolean isShowing) {
- final MutableStateFlow<ObservableTransitionState> showingFlow =
- MutableStateFlow(
- new ObservableTransitionState.Idle(
- isShowing ? CommunalScenes.Communal : CommunalScenes.Blank)
- );
- mKosmos.getCommunalSceneInteractor().setTransitionState(showingFlow);
- mTestScope.getTestScheduler().runCurrent();
- }
-
- private void setQsExpanded(boolean isExpanded) {
- mKosmos.getShadeRepository().setQsExpansion(isExpanded ? 1.0f : 0.0f);
- mTestScope.getTestScheduler().runCurrent();
- }
-
- private void setPulsing(boolean pulsing) {
- mStatusBarStateListener.onPulsingChanged(pulsing);
- }
-
- private void setFullyDozed(boolean fullyDozed) {
- float dozeAmount = fullyDozed ? 1 : 0;
- mStatusBarStateListener.onDozeAmountChanged(dozeAmount, dozeAmount);
- }
-
- private void setSleepy(boolean sleepy) {
- if (sleepy) {
- mWakefulnessObserver.onFinishedGoingToSleep();
- } else {
- mWakefulnessObserver.onStartedWakingUp();
- }
- }
-
- private void setPanelExpanded(boolean expanded) {
- setPanelExpandedAndLockscreenShowing(expanded, /* lockscreenShowing = */ 0.0f);
- }
-
- private void setLockscreenShowing(float lockscreenShowing) {
- setPanelExpandedAndLockscreenShowing(/* panelExpanded = */ false, lockscreenShowing);
- }
-
- private void setPanelExpandedAndLockscreenShowing(boolean panelExpanded,
- float lockscreenShowing) {
- if (SceneContainerFlag.isEnabled()) {
- mStatusBarStateListener.onExpandedChanged(panelExpanded);
- mKosmos.getKeyguardTransitionRepository().sendTransitionStepJava(
- mTestScope,
- makeLockscreenTransitionStep(lockscreenShowing),
- /* validateStep = */ false);
- } else {
- mStatusBarStateListener.onExpandedChanged(panelExpanded || lockscreenShowing > 0.0f);
- }
- }
-
- private TransitionStep makeLockscreenTransitionStep(float value) {
- if (value <= 0.0f) {
- return new TransitionStep(KeyguardState.GONE);
- } else if (value >= 1.0f) {
- return new TransitionStep(KeyguardState.LOCKSCREEN);
- } else {
- return new TransitionStep(
- KeyguardState.GONE,
- KeyguardState.LOCKSCREEN,
- value,
- TransitionState.RUNNING);
- }
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.kt
new file mode 100644
index 0000000..ef0a416
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.kt
@@ -0,0 +1,1007 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification.collection.coordinator
+
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
+import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.concurrency.fakeExecutor
+import com.android.systemui.flags.BrokenWithSceneContainer
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.flags.andSceneContainer
+import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.wakefulnessLifecycle
+import com.android.systemui.kosmos.testCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.plugins.statusbar.statusBarStateController
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.shade.data.repository.ShadeAnimationRepository
+import com.android.systemui.shade.data.repository.fakeShadeRepository
+import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractorLegacyImpl
+import com.android.systemui.shade.domain.interactor.shadeAnimationInteractor
+import com.android.systemui.statusbar.notification.VisibilityLocationProvider
+import com.android.systemui.statusbar.notification.collection.GroupEntry
+import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable.PluggableListener
+import com.android.systemui.statusbar.notification.collection.notifPipeline
+import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository
+import com.android.systemui.statusbar.notification.stack.data.repository.FakeHeadsUpNotificationRepository
+import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository
+import com.android.systemui.statusbar.notification.visibilityLocationProvider
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.statusbar.policy.keyguardStateController
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.withArgCaptor
+import com.android.systemui.util.time.fakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.runTest
+import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+import org.mockito.verification.VerificationMode
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+
+@SmallTest
+@RunWith(ParameterizedAndroidJunit4::class)
+@RunWithLooper
+class VisualStabilityCoordinatorTest(flags: FlagsParameterization) : SysuiTestCase() {
+ private val kosmos = testKosmos().apply { testCase = this@VisualStabilityCoordinatorTest }
+
+ private val invalidateListener: PluggableListener<NotifStabilityManager> = mock()
+ private val headsUpRepository = kosmos.headsUpNotificationRepository
+ private val visibilityLocationProvider: VisibilityLocationProvider =
+ kosmos.visibilityLocationProvider
+ private val keyguardStateController: KeyguardStateController = kosmos.keyguardStateController
+
+ private val fakeSystemClock = kosmos.fakeSystemClock
+ private val fakeBackgroundExecutor = kosmos.fakeExecutor
+ private val testScope = kosmos.testScope
+
+ private val shadeRepository = kosmos.fakeShadeRepository
+ private lateinit var wakefulnessObserver: WakefulnessLifecycle.Observer
+ private lateinit var statusBarStateListener: StatusBarStateController.StateListener
+ private lateinit var notifStabilityManager: NotifStabilityManager
+ private lateinit var entry: NotificationEntry
+ private lateinit var groupEntry: GroupEntry
+
+ private val underTest: VisualStabilityCoordinator by lazy { kosmos.visualStabilityCoordinator }
+
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return FlagsParameterization.allCombinationsOf(Flags.FLAG_STABILIZE_HEADS_UP_GROUP_V2)
+ .andSceneContainer()
+ }
+ }
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
+
+ @Before
+ fun setUp() {
+ configureKosmos()
+
+ underTest.attach(kosmos.notifPipeline)
+ testScope.testScheduler.runCurrent()
+
+ // capture arguments:
+ wakefulnessObserver = withArgCaptor {
+ verify(kosmos.wakefulnessLifecycle).addObserver(capture())
+ }
+ statusBarStateListener = withArgCaptor {
+ verify(kosmos.statusBarStateController).addCallback(capture())
+ }
+ notifStabilityManager = withArgCaptor {
+ verify(kosmos.notifPipeline).setVisualStabilityManager(capture())
+ }
+ notifStabilityManager.setInvalidationListener(invalidateListener)
+
+ entry = NotificationEntryBuilder().setPkg("testPkg1").build()
+
+ groupEntry = GroupEntryBuilder().setSummary(entry).build()
+
+ // Whenever we invalidate, the pipeline runs again, so we invalidate the state
+ whenever(invalidateListener.onPluggableInvalidated(eq(notifStabilityManager), any()))
+ .doAnswer { _ ->
+ notifStabilityManager.onBeginRun()
+ null
+ }
+ }
+
+ private fun configureKosmos() {
+ kosmos.statusBarStateController = mock()
+ // TODO(377868472) only override this when SceneContainer is disabled
+ kosmos.shadeAnimationInteractor =
+ ShadeAnimationInteractorLegacyImpl(ShadeAnimationRepository(), shadeRepository)
+ }
+
+ @Test
+ fun testScreenOff_groupAndSectionChangesAllowed() =
+ testScope.runTest {
+ // GIVEN screen is off, panel isn't expanded and device isn't pulsing
+ setFullyDozed(true)
+ setSleepy(true)
+ setPanelExpanded(false)
+ setPulsing(false)
+
+ // THEN group changes are allowed
+ assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isTrue()
+ assertThat(notifStabilityManager.isGroupPruneAllowed(groupEntry)).isTrue()
+
+ // THEN section changes are allowed
+ assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isTrue()
+ }
+
+ @Test
+ fun testScreenTurningOff_groupAndSectionChangesNotAllowed() =
+ testScope.runTest {
+ // GIVEN the screen is turning off (sleepy but partially dozed)
+ setFullyDozed(false)
+ setSleepy(true)
+ setPanelExpanded(true)
+ setPulsing(false)
+
+ // THEN group changes are NOT allowed
+ assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isFalse()
+ assertThat(notifStabilityManager.isGroupPruneAllowed(groupEntry)).isFalse()
+
+ // THEN section changes are NOT allowed
+ assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isFalse()
+ }
+
+ @Test
+ fun testScreenTurningOn_groupAndSectionChangesNotAllowed() =
+ testScope.runTest {
+ // GIVEN the screen is turning on (still fully dozed, not sleepy)
+ setFullyDozed(true)
+ setSleepy(false)
+ setPanelExpanded(true)
+ setPulsing(false)
+
+ // THEN group changes are NOT allowed
+ assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isFalse()
+ assertThat(notifStabilityManager.isGroupPruneAllowed(groupEntry)).isFalse()
+
+ // THEN section changes are NOT allowed
+ assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isFalse()
+ }
+
+ @Test
+ fun testPanelNotExpanded_groupAndSectionChangesAllowed() =
+ testScope.runTest {
+ // GIVEN screen is on but the panel isn't expanded and device isn't pulsing
+ setFullyDozed(false)
+ setSleepy(false)
+ setPanelExpanded(false)
+ setPulsing(false)
+
+ // THEN group changes are allowed
+ assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isTrue()
+ assertThat(notifStabilityManager.isGroupPruneAllowed(groupEntry)).isTrue()
+
+ // THEN section changes are allowed
+ assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isTrue()
+ }
+
+ @Test
+ fun testPanelExpanded_groupAndSectionChangesNotAllowed() =
+ testScope.runTest {
+ // GIVEN the panel true expanded and device isn't pulsing
+ setFullyDozed(false)
+ setSleepy(false)
+ setPanelExpanded(true)
+ setPulsing(false)
+
+ // THEN group changes are NOT allowed
+ assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isFalse()
+ assertThat(notifStabilityManager.isGroupPruneAllowed(groupEntry)).isFalse()
+
+ // THEN section changes are NOT allowed
+ assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isFalse()
+ }
+
+ @Test
+ @BrokenWithSceneContainer(bugId = 377868472) // mReorderingAllowed is broken with SceneContainer
+ fun testLockscreenPartlyShowing_groupAndSectionChangesNotAllowed() =
+ testScope.runTest {
+ // GIVEN the panel true expanded and device isn't pulsing
+ setFullyDozed(false)
+ setSleepy(false)
+ setLockscreenShowing(0.5f)
+ setPulsing(false)
+
+ // THEN group changes are NOT allowed
+ assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isFalse()
+ assertThat(notifStabilityManager.isGroupPruneAllowed(groupEntry)).isFalse()
+
+ // THEN section changes are NOT allowed
+ assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isFalse()
+ }
+
+ @Test
+ @BrokenWithSceneContainer(bugId = 377868472) // mReorderingAllowed is broken with SceneContainer
+ fun testLockscreenFullyShowing_groupAndSectionChangesNotAllowed() =
+ testScope.runTest {
+ // GIVEN the panel true expanded and device isn't pulsing
+ setFullyDozed(false)
+ setSleepy(false)
+ setLockscreenShowing(1.0f)
+ setPulsing(false)
+
+ // THEN group changes are NOT allowed
+ assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isFalse()
+ assertThat(notifStabilityManager.isGroupPruneAllowed(groupEntry)).isFalse()
+
+ // THEN section changes are NOT allowed
+ assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isFalse()
+ }
+
+ @Test
+ fun testPulsing_screenOff_groupAndSectionChangesNotAllowed() =
+ testScope.runTest {
+ // GIVEN the device is pulsing and screen is off
+ setFullyDozed(true)
+ setSleepy(true)
+ setPulsing(true)
+
+ // THEN group changes are NOT allowed
+ assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isFalse()
+ assertThat(notifStabilityManager.isGroupPruneAllowed(groupEntry)).isFalse()
+
+ // THEN section changes are NOT allowed
+ assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isFalse()
+ }
+
+ @Test
+ fun testPulsing_panelNotExpanded_groupAndSectionChangesNotAllowed() =
+ testScope.runTest {
+ // GIVEN the device is pulsing and screen is off with the panel not expanded
+ setFullyDozed(true)
+ setSleepy(true)
+ setPanelExpanded(false)
+ setPulsing(true)
+
+ // THEN group changes are NOT allowed
+ assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isFalse()
+ assertThat(notifStabilityManager.isGroupPruneAllowed(groupEntry)).isFalse()
+
+ // THEN section changes are NOT allowed
+ assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isFalse()
+ }
+
+ @Test
+ fun testOverrideReorderingSuppression_onlySectionChangesAllowed() =
+ testScope.runTest {
+ // GIVEN section changes typically wouldn't be allowed because the panel is expanded and
+ // we're not pulsing
+ setFullyDozed(false)
+ setSleepy(false)
+ setPanelExpanded(true)
+ setPulsing(true)
+
+ // WHEN we temporarily allow section changes for this notification entry
+ underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.currentTimeMillis())
+
+ // THEN group changes aren't allowed
+ assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isFalse()
+ assertThat(notifStabilityManager.isGroupPruneAllowed(groupEntry)).isFalse()
+
+ // THEN section changes are allowed for this notification but not other notifications
+ assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isTrue()
+ assertThat(
+ notifStabilityManager.isSectionChangeAllowed(
+ NotificationEntryBuilder().setPkg("testPkg2").build()
+ )
+ )
+ .isFalse()
+ }
+
+ @Test
+ fun testTemporarilyAllowSectionChanges_callsInvalidate() =
+ testScope.runTest {
+ // GIVEN section changes typically wouldn't be allowed because the panel is expanded
+ setFullyDozed(false)
+ setSleepy(false)
+ setPanelExpanded(true)
+ setPulsing(false)
+
+ // WHEN we temporarily allow section changes for this notification entry
+ underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.uptimeMillis())
+
+ // THEN the notification list is invalidated
+ verifyStabilityManagerWasInvalidated(times(1))
+ }
+
+ @Test
+ fun testTemporarilyAllowSectionChanges_noInvalidationCalled() =
+ testScope.runTest {
+ // GIVEN section changes typically WOULD be allowed
+ setFullyDozed(true)
+ setSleepy(true)
+ setPanelExpanded(false)
+ setPulsing(false)
+
+ // WHEN we temporarily allow section changes for this notification entry
+ underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.currentTimeMillis())
+
+ // THEN invalidate is not called because this entry was never suppressed from reordering
+ verifyStabilityManagerWasInvalidated(never())
+ }
+
+ @Test
+ fun testTemporarilyAllowSectionChangesTimeout() =
+ testScope.runTest {
+ // GIVEN section changes typically WOULD be allowed
+ setFullyDozed(true)
+ setSleepy(true)
+ setPanelExpanded(false)
+ setPulsing(false)
+ assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isTrue()
+
+ // WHEN we temporarily allow section changes for this notification entry
+ underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.currentTimeMillis())
+
+ // THEN invalidate is not called because this entry was never suppressed from
+ // reordering;
+ // THEN section changes are allowed for this notification
+ verifyStabilityManagerWasInvalidated(never())
+ assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isTrue()
+
+ // WHEN we're pulsing (now disallowing reordering)
+ setPulsing(true)
+
+ // THEN we're still allowed to reorder this section because it's still in the list of
+ // notifications to allow section changes
+ assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isTrue()
+
+ // WHEN the timeout for the temporarily allow section reordering runnable is finsihed
+ fakeBackgroundExecutor.advanceClockToNext()
+ fakeBackgroundExecutor.runNextReady()
+
+ // THEN section changes aren't allowed anymore
+ assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isFalse()
+ }
+
+ @Test
+ fun testTemporarilyAllowSectionChanges_isPulsingChangeBeforeTimeout() =
+ testScope.runTest {
+ // GIVEN section changes typically wouldn't be allowed because the device is pulsing
+ setFullyDozed(true)
+ setSleepy(true)
+ setPanelExpanded(false)
+ setPulsing(true)
+
+ // WHEN we temporarily allow section changes for this notification entry
+ underTest.temporarilyAllowSectionChanges(entry, fakeSystemClock.currentTimeMillis())
+ // can now reorder, so invalidates
+ verifyStabilityManagerWasInvalidated(times(1))
+
+ // WHEN reordering is now allowed because device isn't pulsing anymore
+ setPulsing(false)
+
+ // THEN invalidate isn't called a second time since reordering was already allowed
+ verifyStabilityManagerWasInvalidated(times(1))
+ }
+
+ @Test
+ fun testMovingVisibleHeadsUpNotAllowed() =
+ testScope.runTest {
+ // GIVEN stability enforcing conditions
+ setPanelExpanded(true)
+ setSleepy(false)
+
+ // WHEN a notification is alerting and visible
+ headsUpRepository.setHeadsUpKeys(entry.key)
+ whenever(visibilityLocationProvider.isInVisibleLocation(any())).thenReturn(true)
+
+ // THEN the notification cannot be reordered
+ assertThat(notifStabilityManager.isEntryReorderingAllowed(entry)).isFalse()
+ assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isFalse()
+ }
+
+ @Test
+ fun testMovingInvisibleHeadsUpAllowed() =
+ testScope.runTest {
+ // GIVEN stability enforcing conditions
+ setPanelExpanded(true)
+ setSleepy(false)
+
+ // WHEN a notification is alerting but not visible
+ headsUpRepository.setHeadsUpKeys(entry.key)
+ whenever(visibilityLocationProvider.isInVisibleLocation(any())).thenReturn(false)
+
+ // THEN the notification can be reordered
+ assertThat(notifStabilityManager.isEntryReorderingAllowed(entry)).isTrue()
+ assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isTrue()
+ }
+
+ @Test
+ fun testNeverSuppressedChanges_noInvalidationCalled() =
+ testScope.runTest {
+ // GIVEN no notifications are currently being suppressed from grouping nor being sorted
+
+ // WHEN device isn't pulsing anymore
+
+ setPulsing(false)
+
+ // WHEN fully dozed
+ setFullyDozed(true)
+
+ // WHEN sleepy
+ setSleepy(true)
+
+ // WHEN panel isn't expanded
+ setPanelExpanded(false)
+
+ // THEN we never see any calls to invalidate since there weren't any notifications that
+ // were being suppressed from grouping or section changes
+ verifyStabilityManagerWasInvalidated(never())
+ }
+
+ @Test
+ fun testNotSuppressingGroupChangesAnymore_invalidationCalled() =
+ testScope.runTest {
+ // GIVEN visual stability is being maintained b/c panel is expanded
+ setPulsing(false)
+ setFullyDozed(false)
+ setSleepy(false)
+ setPanelExpanded(true)
+
+ assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isFalse()
+ assertThat(notifStabilityManager.isGroupPruneAllowed(groupEntry)).isFalse()
+
+ // WHEN the panel isn't expanded anymore
+ setPanelExpanded(false)
+
+ // THEN invalidate is called because we were previously suppressing a group change
+ verifyStabilityManagerWasInvalidated(times(1))
+ }
+
+ @Test
+ fun testNotLaunchingActivityAnymore_invalidationCalled() {
+ // GIVEN visual stability is being maintained b/c animation is playing
+ setActivityLaunching(true)
+
+ assertThat(notifStabilityManager.isPipelineRunAllowed()).isFalse()
+
+ // WHEN the animation has stopped playing
+ setActivityLaunching(false)
+
+ // THEN invalidate is called, b/c we were previously suppressing the pipeline from running
+ verifyStabilityManagerWasInvalidated(times(1))
+ }
+
+ @Test
+ fun testNotCollapsingPanelAnymore_invalidationCalled() {
+ // GIVEN visual stability is being maintained b/c animation is playing
+ setPanelCollapsing(true)
+
+ assertThat(notifStabilityManager.isPipelineRunAllowed()).isFalse()
+
+ // WHEN the animation has stopped playing
+ setPanelCollapsing(false)
+
+ // THEN invalidate is called, b/c we were previously suppressing the pipeline from running
+ verifyStabilityManagerWasInvalidated(times(1))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_CHECK_LOCKSCREEN_GONE_TRANSITION)
+ @DisableSceneContainer
+ fun testNotLockscreenInGoneTransitionLegacy_invalidationCalled() {
+ // GIVEN visual stability is being maintained b/c animation is playing
+ whenever(keyguardStateController.isKeyguardFadingAway).thenReturn(true)
+ underTest.mKeyguardFadeAwayAnimationCallback.onKeyguardFadingAwayChanged()
+
+ assertThat(notifStabilityManager.isPipelineRunAllowed()).isFalse()
+
+ // WHEN the animation has stopped playing
+ whenever(keyguardStateController.isKeyguardFadingAway).thenReturn(false)
+ underTest.mKeyguardFadeAwayAnimationCallback.onKeyguardFadingAwayChanged()
+
+ // THEN invalidate is called, b/c we were previously suppressing the pipeline from running
+ verifyStabilityManagerWasInvalidated(times(1))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_CHECK_LOCKSCREEN_GONE_TRANSITION)
+ @EnableSceneContainer
+ @BrokenWithSceneContainer(bugId = 377868472) // mReorderingAllowed is broken with SceneContainer
+ fun testNotLockscreenInGoneTransition_invalidationCalled() =
+ testScope.runTest {
+ // GIVEN visual stability is being maintained b/c animation is playing
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ KeyguardState.LOCKSCREEN,
+ KeyguardState.GONE,
+ 1f,
+ TransitionState.RUNNING,
+ ),
+ /* validateStep = */ false,
+ )
+ testScope.testScheduler.runCurrent()
+ assertThat(notifStabilityManager.isPipelineRunAllowed()).isFalse()
+
+ // WHEN the animation has stopped playing
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ KeyguardState.LOCKSCREEN,
+ KeyguardState.GONE,
+ 1f,
+ TransitionState.FINISHED,
+ ),
+ /* validateStep = */ false,
+ )
+ testScope.testScheduler.runCurrent()
+
+ // THEN invalidate is called, b/c we were previously suppressing the pipeline from
+ // running
+ verifyStabilityManagerWasInvalidated(times(1))
+ }
+
+ @Test
+ fun testNeverSuppressPipelineRunFromPanelCollapse_noInvalidationCalled() {
+ // GIVEN animation is playing
+ setPanelCollapsing(true)
+
+ // WHEN the animation has stopped playing
+ setPanelCollapsing(false)
+
+ // THEN invalidate is not called, b/c nothing has been suppressed
+ verifyStabilityManagerWasInvalidated(never())
+ }
+
+ @Test
+ fun testNeverSuppressPipelineRunFromLaunchActivity_noInvalidationCalled() {
+ // GIVEN animation is playing
+ setActivityLaunching(true)
+
+ // WHEN the animation has stopped playing
+ setActivityLaunching(false)
+
+ // THEN invalidate is not called, b/c nothing has been suppressed
+ verifyStabilityManagerWasInvalidated(never())
+ }
+
+ @Test
+ fun testNotSuppressingEntryReorderingAnymoreWillInvalidate() =
+ testScope.runTest {
+ // GIVEN visual stability is being maintained b/c panel is expanded
+ setPulsing(false)
+ setFullyDozed(false)
+ setSleepy(false)
+ setPanelExpanded(true)
+ setCommunalShowing(false)
+
+ assertThat(notifStabilityManager.isEntryReorderingAllowed(entry)).isFalse()
+ // The pipeline still has to report back that entry reordering was suppressed
+ notifStabilityManager.onEntryReorderSuppressed()
+
+ // WHEN the panel isn't expanded anymore
+ setPanelExpanded(false)
+
+ // THEN invalidate is called because we were previously suppressing an entry reorder
+ verifyStabilityManagerWasInvalidated(times(1))
+ }
+
+ @Test
+ @BrokenWithSceneContainer(bugId = 377868472) // mReorderingAllowed is broken with SceneContainer
+ fun testCommunalShowingWillNotSuppressReordering() =
+ testScope.runTest {
+ // GIVEN panel is expanded, communal is showing, and QS is collapsed
+ setPulsing(false)
+ setFullyDozed(false)
+ setSleepy(false)
+ setPanelExpanded(true)
+ setQsExpanded(false)
+ setCommunalShowing(true)
+
+ // THEN Reordering should be allowed
+ assertThat(notifStabilityManager.isEntryReorderingAllowed(entry)).isTrue()
+ }
+
+ @Test
+ fun testQsExpandedOverCommunalWillSuppressReordering() =
+ testScope.runTest {
+ // GIVEN panel is expanded and communal is showing, but QS is expanded
+ setPulsing(false)
+ setFullyDozed(false)
+ setSleepy(false)
+ setPanelExpanded(true)
+ setQsExpanded(true)
+ setCommunalShowing(true)
+
+ // THEN Reordering should not be allowed
+ assertThat(notifStabilityManager.isEntryReorderingAllowed(entry)).isFalse()
+ }
+
+ @Test
+ fun testQueryingEntryReorderingButNotReportingReorderSuppressedDoesNotInvalidate() =
+ testScope.runTest {
+ // GIVEN visual stability is being maintained b/c panel is expanded
+ setPulsing(false)
+ setFullyDozed(false)
+ setSleepy(false)
+ setPanelExpanded(true)
+
+ assertThat(notifStabilityManager.isEntryReorderingAllowed(entry)).isFalse()
+
+ // WHEN the panel isn't expanded anymore
+ setPanelExpanded(false)
+
+ // THEN invalidate is not called because we were not told that an entry reorder was
+ // suppressed
+ verifyStabilityManagerWasInvalidated(never())
+ }
+
+ @Test
+ fun testHeadsUp_allowedToChangeGroupAndSection() =
+ testScope.runTest {
+ // GIVEN group + section changes disallowed
+ setFullyDozed(false)
+ setSleepy(false)
+ setPanelExpanded(true)
+ setPulsing(true)
+ assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isFalse()
+ assertThat(notifStabilityManager.isGroupPruneAllowed(groupEntry)).isFalse()
+ assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isFalse()
+
+ // GIVEN mEntry is a HUN
+ headsUpRepository.setHeadsUpKeys(entry.key)
+
+ // THEN group + section changes are allowed
+ assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isTrue()
+ assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isTrue()
+
+ // BUT pruning the group for which this is the summary would still NOT be allowed.
+ assertThat(notifStabilityManager.isGroupPruneAllowed(groupEntry)).isFalse()
+ }
+
+ @Test
+ fun everyChangeAllowed_onReorderingEnabled_legacy() =
+ testScope.runTest {
+ assumeFalse(StabilizeHeadsUpGroup.isEnabled)
+ // GIVEN - reordering is allowed.
+ setPulsing(false)
+ setPanelExpanded(false)
+
+ // THEN
+ assertThat(notifStabilityManager.isEveryChangeAllowed()).isTrue()
+ assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isTrue()
+ assertThat(notifStabilityManager.isGroupPruneAllowed(groupEntry)).isTrue()
+ assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isTrue()
+ assertThat(notifStabilityManager.isEntryReorderingAllowed(entry)).isTrue()
+ }
+
+ @Test
+ fun everyChangeAllowed_noActiveHeadsUpGroup_onReorderingEnabled() =
+ testScope.runTest {
+ assumeTrue(StabilizeHeadsUpGroup.isEnabled)
+ // GIVEN - reordering is allowed.
+ setPulsing(false)
+ setPanelExpanded(false)
+
+ // GIVEN - empty heads-up-group keys
+ underTest.setHeadsUpGroupKeys(setOf())
+
+ // THEN
+ assertThat(notifStabilityManager.isEveryChangeAllowed()).isTrue()
+ assertThat(notifStabilityManager.isGroupChangeAllowed(entry)).isTrue()
+ assertThat(notifStabilityManager.isGroupPruneAllowed(groupEntry)).isTrue()
+ assertThat(notifStabilityManager.isSectionChangeAllowed(entry)).isTrue()
+ assertThat(notifStabilityManager.isEntryReorderingAllowed(entry)).isTrue()
+ }
+
+ @Test
+ fun everyChangeDisallowed_activeHeadsUpGroup_onReorderingEnabled() =
+ testScope.runTest {
+ assumeTrue(StabilizeHeadsUpGroup.isEnabled)
+ // GIVEN - reordering is allowed.
+ setPulsing(false)
+ setPanelExpanded(false)
+
+ // GIVEN - there is a group heads-up.
+ underTest.setHeadsUpGroupKeys(setOf("heads_up_group_key"))
+
+ // THEN
+ assertThat(notifStabilityManager.isEveryChangeAllowed()).isFalse()
+ }
+
+ @Test
+ fun nonHeadsUpGroup_changesAllowed_onReorderingEnabled() =
+ testScope.runTest {
+ assumeTrue(StabilizeHeadsUpGroup.isEnabled)
+ // GIVEN - reordering is allowed.
+ setPulsing(false)
+ setPanelExpanded(false)
+
+ // GIVEN - there is a group heads-up.
+ val headsUpGroupKey = "heads_up_group_key"
+ underTest.setHeadsUpGroupKeys(setOf(headsUpGroupKey))
+ headsUpRepository.setHeadsUpKeys(headsUpGroupKey)
+
+ // GIVEN - HUN Group Summary
+ val nonHeadsUpGroupSummary: NotificationEntry = mock()
+ whenever(nonHeadsUpGroupSummary.key).thenReturn("non_heads_up_group_key")
+ whenever(nonHeadsUpGroupSummary.isSummaryWithChildren).thenReturn(true)
+ val nonHeadsUpGroupEntry: GroupEntry = mock()
+ whenever(nonHeadsUpGroupEntry.summary).thenReturn(nonHeadsUpGroupSummary)
+ whenever(nonHeadsUpGroupEntry.representativeEntry).thenReturn(nonHeadsUpGroupSummary)
+
+ // THEN
+ assertThat(notifStabilityManager.isGroupPruneAllowed(nonHeadsUpGroupEntry)).isTrue()
+ assertThat(notifStabilityManager.isEntryReorderingAllowed(nonHeadsUpGroupEntry))
+ .isTrue()
+ }
+
+ @Test
+ fun headsUpGroup_changesDisallowed_onReorderingEnabled() =
+ testScope.runTest {
+ assumeTrue(StabilizeHeadsUpGroup.isEnabled)
+ // GIVEN - reordering is allowed.
+ setPulsing(false)
+ setPanelExpanded(false)
+
+ // GIVEN - there is a group heads-up.
+ val headsUpGroupKey = "heads_up_group_key"
+ underTest.setHeadsUpGroupKeys(setOf(headsUpGroupKey))
+ headsUpRepository.setHeadsUpKeys(headsUpGroupKey)
+
+ // GIVEN - HUN Group
+ val headsUpGroupSummary: NotificationEntry = mock()
+ whenever(headsUpGroupSummary.rowIsChildInGroup()).thenReturn(false)
+ whenever(headsUpGroupSummary.key).thenReturn(headsUpGroupKey)
+ whenever(headsUpGroupSummary.isSummaryWithChildren).thenReturn(true)
+
+ val headsUpGroupEntry: GroupEntry = mock()
+ whenever(headsUpGroupEntry.summary).thenReturn(headsUpGroupSummary)
+ whenever(headsUpGroupEntry.representativeEntry).thenReturn(headsUpGroupSummary)
+
+ whenever(headsUpGroupSummary.parent).thenReturn(headsUpGroupEntry)
+
+ // GIVEN - HUN is in visible location
+ whenever(visibilityLocationProvider.isInVisibleLocation(headsUpGroupSummary))
+ .thenReturn(true)
+
+ // THEN
+ assertThat(notifStabilityManager.isGroupPruneAllowed(headsUpGroupEntry)).isFalse()
+ assertThat(notifStabilityManager.isEntryReorderingAllowed(headsUpGroupEntry)).isFalse()
+ }
+
+ @Test
+ fun headsUpGroupSummaries_changesDisallowed_onReorderingEnabled() =
+ testScope.runTest {
+ assumeTrue(StabilizeHeadsUpGroup.isEnabled)
+ // GIVEN - reordering is allowed.
+ setPulsing(false)
+ setPanelExpanded(false)
+
+ // GIVEN - there is a group heads-up.
+ val headsUpGroupKey = "heads_up_group_key"
+ underTest.setHeadsUpGroupKeys(setOf(headsUpGroupKey))
+ headsUpRepository.setHeadsUpKeys(headsUpGroupKey)
+
+ // GIVEN - HUN Group
+ val headsUpGroupSummary: NotificationEntry = mock()
+ whenever(headsUpGroupSummary.rowIsChildInGroup()).thenReturn(false)
+ whenever(headsUpGroupSummary.key).thenReturn(headsUpGroupKey)
+ whenever(headsUpGroupSummary.isSummaryWithChildren).thenReturn(true)
+
+ val headsUpGroupEntry: GroupEntry = mock()
+ whenever(headsUpGroupEntry.summary).thenReturn(headsUpGroupSummary)
+ whenever(headsUpGroupEntry.representativeEntry).thenReturn(headsUpGroupSummary)
+
+ whenever(headsUpGroupSummary.parent).thenReturn(headsUpGroupEntry)
+
+ // GIVEN - HUN is in visible location
+ whenever(visibilityLocationProvider.isInVisibleLocation(headsUpGroupSummary))
+ .thenReturn(true)
+
+ // THEN
+ assertThat(notifStabilityManager.isGroupChangeAllowed(headsUpGroupSummary)).isFalse()
+ assertThat(notifStabilityManager.isEntryReorderingAllowed(headsUpGroupSummary))
+ .isFalse()
+ assertThat(notifStabilityManager.isSectionChangeAllowed(headsUpGroupSummary)).isFalse()
+ }
+
+ @Test
+ fun notificationInNonHUNGroup_changesAllowed_onReorderingEnabled() =
+ testScope.runTest {
+ assumeTrue(StabilizeHeadsUpGroup.isEnabled)
+ // GIVEN - reordering is allowed.
+ setPulsing(false)
+ setPanelExpanded(false)
+
+ // GIVEN - there is a group heads-up.
+ val headsUpGroupKey = "heads_up_group_key"
+ underTest.setHeadsUpGroupKeys(setOf(headsUpGroupKey))
+ headsUpRepository.setHeadsUpKeys(headsUpGroupKey)
+
+ // GIVEN - non HUN parent Group Summary
+ val groupSummary: NotificationEntry = mock()
+ whenever(groupSummary.key).thenReturn("non_heads_up_group_key")
+ whenever(groupSummary.isSummaryWithChildren).thenReturn(true)
+
+ val nonHeadsUpGroupEntry: GroupEntry = mock()
+ whenever(nonHeadsUpGroupEntry.summary).thenReturn(groupSummary)
+ whenever(nonHeadsUpGroupEntry.representativeEntry).thenReturn(groupSummary)
+
+ // GIVEN - child entry in a non heads-up group.
+ val childEntry: NotificationEntry = mock()
+ whenever(childEntry.rowIsChildInGroup()).thenReturn(true)
+ whenever(childEntry.parent).thenReturn(nonHeadsUpGroupEntry)
+ whenever(childEntry.parent).thenReturn(nonHeadsUpGroupEntry)
+
+ // THEN
+ assertThat(notifStabilityManager.isGroupChangeAllowed(childEntry)).isTrue()
+ assertThat(notifStabilityManager.isSectionChangeAllowed(childEntry)).isTrue()
+ assertThat(notifStabilityManager.isEntryReorderingAllowed(nonHeadsUpGroupEntry))
+ .isTrue()
+ }
+
+ @Test
+ fun notificationInHUNGroup_changesDisallowed_reorderingEnabled() =
+ testScope.runTest {
+ assumeTrue(StabilizeHeadsUpGroup.isEnabled)
+ // GIVEN - reordering is allowed.
+ setPulsing(false)
+ setPanelExpanded(false)
+
+ // GIVEN - there is a group heads-up.
+ val headsUpGroupKey = "heads_up_group_key"
+ underTest.setHeadsUpGroupKeys(setOf(headsUpGroupKey))
+ headsUpRepository.setHeadsUpKeys(headsUpGroupKey)
+
+ // GIVEN - HUN Group Summary
+ val headsUpGroupSummary: NotificationEntry = mock()
+ whenever(headsUpGroupSummary.rowIsChildInGroup()).thenReturn(false)
+ whenever(headsUpGroupSummary.key).thenReturn(headsUpGroupKey)
+ whenever(headsUpGroupSummary.isSummaryWithChildren).thenReturn(true)
+
+ val nonHeadsUpGroupEntry: GroupEntry = mock()
+ whenever(nonHeadsUpGroupEntry.summary).thenReturn(headsUpGroupSummary)
+ whenever(nonHeadsUpGroupEntry.representativeEntry).thenReturn(headsUpGroupSummary)
+
+ // GIVEN - child entry in a non heads-up group.
+ val childEntry: NotificationEntry =
+ mock<NotificationEntry>().apply { whenever(key).thenReturn("child") }
+ whenever(childEntry.rowIsChildInGroup()).thenReturn(true)
+ whenever(childEntry.parent).thenReturn(nonHeadsUpGroupEntry)
+
+ // GIVEN - HUN is in visible location
+ whenever(visibilityLocationProvider.isInVisibleLocation(headsUpGroupSummary))
+ .thenReturn(true)
+
+ // THEN
+ assertThat(notifStabilityManager.isGroupChangeAllowed(childEntry)).isFalse()
+ assertThat(notifStabilityManager.isSectionChangeAllowed(childEntry)).isFalse()
+ assertThat(notifStabilityManager.isEntryReorderingAllowed(childEntry)).isFalse()
+ }
+
+ private fun verifyStabilityManagerWasInvalidated(mode: VerificationMode) {
+ verify(invalidateListener, mode).onPluggableInvalidated(eq(notifStabilityManager), any())
+ }
+
+ private fun setActivityLaunching(activityLaunching: Boolean) {
+ kosmos.shadeAnimationInteractor.setIsLaunchingActivity(activityLaunching)
+ testScope.testScheduler.runCurrent()
+ }
+
+ private fun setPanelCollapsing(collapsing: Boolean) {
+ shadeRepository.setLegacyIsClosing(collapsing)
+ testScope.testScheduler.runCurrent()
+ }
+
+ private fun setCommunalShowing(isShowing: Boolean) {
+ val showingFlow =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(
+ if (isShowing) CommunalScenes.Communal else CommunalScenes.Blank
+ )
+ )
+ kosmos.communalSceneInteractor.setTransitionState(showingFlow)
+ testScope.testScheduler.runCurrent()
+ }
+
+ private fun setQsExpanded(isExpanded: Boolean) {
+ kosmos.shadeRepository.setQsExpansion(if (isExpanded) 1.0f else 0.0f)
+ testScope.testScheduler.runCurrent()
+ }
+
+ private fun setPulsing(pulsing: Boolean) = statusBarStateListener.onPulsingChanged(pulsing)
+
+ private fun setFullyDozed(fullyDozed: Boolean) {
+ val dozeAmount = if (fullyDozed) 1f else 0f
+ statusBarStateListener.onDozeAmountChanged(dozeAmount, dozeAmount)
+ }
+
+ private fun setSleepy(sleepy: Boolean) {
+ if (sleepy) {
+ wakefulnessObserver.onFinishedGoingToSleep()
+ } else {
+ wakefulnessObserver.onStartedWakingUp()
+ }
+ }
+
+ private suspend fun setPanelExpanded(expanded: Boolean) =
+ setPanelExpandedAndLockscreenShowing(expanded, /* lockscreenShowing= */ 0.0f)
+
+ private suspend fun setLockscreenShowing(lockscreenShowing: Float) =
+ setPanelExpandedAndLockscreenShowing(/* panelExpanded= */ false, lockscreenShowing)
+
+ private suspend fun setPanelExpandedAndLockscreenShowing(
+ panelExpanded: Boolean,
+ lockscreenShowing: Float,
+ ) {
+ if (SceneContainerFlag.isEnabled) {
+ statusBarStateListener.onExpandedChanged(panelExpanded)
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(
+ makeLockscreenTransitionStep(lockscreenShowing),
+ /* validateStep = */ false,
+ )
+ } else {
+ statusBarStateListener.onExpandedChanged(panelExpanded || lockscreenShowing > 0.0f)
+ }
+ }
+
+ private fun makeLockscreenTransitionStep(value: Float): TransitionStep {
+ return when (value) {
+ 0.0f -> TransitionStep(KeyguardState.GONE)
+ 1.0f -> TransitionStep(KeyguardState.LOCKSCREEN)
+ else ->
+ TransitionStep(
+ KeyguardState.GONE,
+ KeyguardState.LOCKSCREEN,
+ value,
+ TransitionState.RUNNING,
+ )
+ }
+ }
+}
+
+private fun FakeHeadsUpNotificationRepository.setHeadsUpKeys(vararg keys: String) {
+ setNotifications(keys.map { FakeHeadsUpRowRepository(key = it) })
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpAnimatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpAnimatorTest.kt
new file mode 100644
index 0000000..206eb89
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpAnimatorTest.kt
@@ -0,0 +1,87 @@
+/*
+ * 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.headsup
+
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import org.junit.Before
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@EnableFlags(NotificationsHunSharedAnimationValues.FLAG_NAME)
+class HeadsUpAnimatorTest : SysuiTestCase() {
+ @Before
+ fun setUp() {
+ context.getOrCreateTestableResources().apply {
+ this.addOverride(R.dimen.heads_up_appear_y_above_screen, TEST_Y_ABOVE_SCREEN)
+ }
+ }
+
+ @Test
+ fun getHeadsUpYTranslation_fromBottomTrue_usesBottomAndYAbove() {
+ val underTest = HeadsUpAnimator(context)
+ underTest.stackTopMargin = 30
+ underTest.headsUpAppearHeightBottom = 300
+
+ val yTranslation = underTest.getHeadsUpYTranslation(isHeadsUpFromBottom = true)
+
+ assertThat(yTranslation).isEqualTo(TEST_Y_ABOVE_SCREEN + 300)
+ }
+
+ @Test
+ fun getHeadsUpYTranslation_fromBottomFalse_usesTopMarginAndYAbove() {
+ val underTest = HeadsUpAnimator(context)
+ underTest.stackTopMargin = 30
+ underTest.headsUpAppearHeightBottom = 300
+
+ val yTranslation = underTest.getHeadsUpYTranslation(isHeadsUpFromBottom = false)
+
+ assertThat(yTranslation).isEqualTo(-30 - TEST_Y_ABOVE_SCREEN)
+ }
+
+ @Test
+ fun getHeadsUpYTranslation_resourcesUpdated() {
+ val underTest = HeadsUpAnimator(context)
+ underTest.stackTopMargin = 30
+ underTest.headsUpAppearHeightBottom = 300
+
+ val yTranslation = underTest.getHeadsUpYTranslation(isHeadsUpFromBottom = true)
+
+ assertThat(yTranslation).isEqualTo(TEST_Y_ABOVE_SCREEN + 300)
+
+ // WHEN the resource is updated
+ val newYAbove = 600
+ context.getOrCreateTestableResources().apply {
+ this.addOverride(R.dimen.heads_up_appear_y_above_screen, newYAbove)
+ }
+ underTest.updateResources(context)
+
+ // THEN HeadsUpAnimator knows about it
+ assertThat(underTest.getHeadsUpYTranslation(isHeadsUpFromBottom = true))
+ .isEqualTo(newYAbove + 300)
+ }
+
+ companion object {
+ private const val TEST_Y_ABOVE_SCREEN = 50
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
index 25ae13f..f060cae 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
@@ -21,6 +21,7 @@
import android.net.Uri
import android.os.UserHandle
import android.os.UserHandle.USER_ALL
+import android.service.notification.StatusBarNotification
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -36,6 +37,7 @@
import com.android.systemui.statusbar.SbnBuilder
import com.android.systemui.statusbar.SmartReplyController
import com.android.systemui.statusbar.notification.ColorUpdateLogger
+import com.android.systemui.statusbar.notification.collection.EntryAdapter
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider
@@ -229,6 +231,10 @@
@Test
fun registerSettingsListener_forBubbles() {
controller.init(mock(NotificationEntry::class.java))
+ val entryAdapter = mock(EntryAdapter::class.java)
+ whenever(entryAdapter.sbn).thenReturn(mock(StatusBarNotification::class.java))
+ whenever(view.entryAdapter).thenReturn(entryAdapter)
+
val viewStateObserver = withArgCaptor {
verify(view).addOnAttachStateChangeListener(capture())
}
@@ -239,6 +245,9 @@
@Test
fun unregisterSettingsListener_forBubbles() {
controller.init(mock(NotificationEntry::class.java))
+ val entryAdapter = mock(EntryAdapter::class.java)
+ whenever(entryAdapter.sbn).thenReturn(mock(StatusBarNotification::class.java))
+ whenever(view.entryAdapter).thenReturn(entryAdapter)
val viewStateObserver = withArgCaptor {
verify(view).addOnAttachStateChangeListener(capture())
}
@@ -255,6 +264,7 @@
@Test
fun settingsListener_invalidUserId() {
+ whenever(view.entryAdapter).thenReturn(mock(EntryAdapter::class.java))
controller.mSettingsListener.onSettingChanged(BUBBLES_SETTING_URI, -1000, "1")
controller.mSettingsListener.onSettingChanged(BUBBLES_SETTING_URI, -1000, null)
@@ -265,6 +275,12 @@
fun settingsListener_validUserId() {
val childView: NotificationContentView = mock()
whenever(view.privateLayout).thenReturn(childView)
+ val entryAdapter = mock(EntryAdapter::class.java)
+ val sbn =
+ SbnBuilder().setNotification(Notification.Builder(mContext).build())
+ .setUser(UserHandle.of(view.entry.sbn.userId)).build()
+ whenever(entryAdapter.sbn).thenReturn(sbn)
+ whenever(view.entryAdapter).thenReturn(entryAdapter)
controller.mSettingsListener.onSettingChanged(
BUBBLES_SETTING_URI,
@@ -293,6 +309,9 @@
.thenReturn(
NotificationEntryBuilder().setSbn(sbn).setUser(UserHandle.of(USER_ALL)).build()
)
+ val entryAdapter = mock(EntryAdapter::class.java)
+ whenever(entryAdapter.sbn).thenReturn(sbn)
+ whenever(view.entryAdapter).thenReturn(entryAdapter)
controller.mSettingsListener.onSettingChanged(BUBBLES_SETTING_URI, 9, "1")
verify(childView).setBubblesEnabledForUser(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java
index 979a1d0..fd49f60 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java
@@ -79,6 +79,7 @@
mRow = spy(mNotificationTestHelper.createRow());
Notification notification = mRow.getEntry().getSbn().getNotification();
notification.contentIntent = mock(PendingIntent.class);
+ when(notification.contentIntent.isActivity()).thenReturn(true);
doReturn(true).when(mRow).startDragAndDrop(any(), any(), any(), anyInt());
mGroupRow = mNotificationTestHelper.createGroup(4);
when(mMenuRow.getLongpressMenuItem(any(Context.class))).thenReturn(mMenuItem);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 92ceb60..c376fad 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -70,6 +70,8 @@
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
+import com.android.systemui.statusbar.notification.collection.GroupEntry;
+import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
@@ -106,6 +108,7 @@
import org.mockito.ArgumentCaptor;
+import java.util.List;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
@@ -338,6 +341,46 @@
}
/**
+ * Returns an {@link GroupEntry} group with the given number of child
+ * notifications.
+ */
+ public GroupEntry createGroupEntry(int numChildren,
+ @Nullable List<NotificationEntry> additionalChildren) {
+ Notification summary = new Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .setGroupSummary(true)
+ .setGroup(GROUP_KEY)
+ .build();
+
+ NotificationEntry summaryEntry = new NotificationEntryBuilder()
+ .setPkg(PKG)
+ .setOpPkg(PKG)
+ .setId(mId++)
+ .setUid(UID)
+ .setInitialPid(2000)
+ .setNotification(summary)
+ .setParent(GroupEntry.ROOT_ENTRY)
+ .build();
+ GroupEntryBuilder groupEntry = new GroupEntryBuilder()
+ .setSummary(summaryEntry);
+
+ for (int i = 0; i < numChildren; i++) {
+ NotificationEntry child = new NotificationEntryBuilder()
+ .setParent(GroupEntry.ROOT_ENTRY)
+ .setNotification(new Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .setGroup(GROUP_KEY)
+ .build())
+ .build();
+ groupEntry.addChild(child);
+ }
+ for (NotificationEntry entry : additionalChildren) {
+ groupEntry.addChild(entry);
+ }
+ return groupEntry.build();
+ }
+
+ /**
* Returns an {@link ExpandableNotificationRow} group with the given number of child
* notifications.
*/
@@ -411,6 +454,23 @@
}
/**
+ * Returns an {@link NotificationEntry} that should be shown as a bubble and is part
+ * of a group of notifications.
+ */
+ public NotificationEntry createBubbleEntryInGroup() throws Exception {
+ Notification n = createNotification(false /* isGroupSummary */,
+ GROUP_KEY /* groupKey */,
+ makeBubbleMetadata(null /* deleteIntent */, false /* autoExpand */));
+ n.flags |= FLAG_BUBBLE;
+ NotificationEntry entry = generateEntry(n, PKG, UID, USER_HANDLE,
+ mDefaultInflationFlags, IMPORTANCE_HIGH);
+ modifyRanking(entry)
+ .setCanBubble(true)
+ .build();
+ return entry;
+ }
+
+ /**
* Returns an {@link ExpandableNotificationRow} that should be shown as a bubble and is part
* of a group of notifications.
*/
@@ -573,6 +633,41 @@
return mKeyguardBypassController;
}
+ private NotificationEntry generateEntry(
+ Notification notification,
+ String pkg,
+ int uid,
+ UserHandle userHandle,
+ @InflationFlag int extraInflationFlags,
+ int importance)
+ throws Exception {
+ final NotificationChannel channel =
+ new NotificationChannel(
+ notification.getChannelId(),
+ notification.getChannelId(),
+ importance);
+ channel.setBlockable(true);
+
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg(pkg)
+ .setOpPkg(pkg)
+ .setId(mId++)
+ .setUid(uid)
+ .setInitialPid(2000)
+ .setNotification(notification)
+ .setUser(userHandle)
+ .setPostTime(System.currentTimeMillis())
+ .setChannel(channel)
+ .updateRanking(rankingBuilder -> rankingBuilder.setIsConversation(
+ notification.isStyle(Notification.MessagingStyle.class)
+ ))
+ .build();
+
+
+ return entry;
+ }
+
+
private ExpandableNotificationRow generateRow(
Notification notification,
String pkg,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt
index 961616c..3d1fdee 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt
@@ -22,6 +22,8 @@
import com.android.systemui.accessibility.data.repository.FakeAccessibilityRepository
import com.android.systemui.accessibility.domain.interactor.AccessibilityInteractor
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.testKosmos
+import com.android.systemui.window.domain.interactor.windowRootViewBlurInteractor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -32,12 +34,16 @@
@SmallTest
class ActivatableNotificationViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+
// fakes
private val a11yRepo = FakeAccessibilityRepository()
// real impls
private val a11yInteractor = AccessibilityInteractor(a11yRepo)
- private val underTest = ActivatableNotificationViewModel(a11yInteractor)
+ private val windowRootViewBlurInteractor = kosmos.windowRootViewBlurInteractor
+ private val underTest = ActivatableNotificationViewModel(a11yInteractor,
+ windowRootViewBlurInteractor)
@Test
fun isTouchable_whenA11yTouchExplorationDisabled() = runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
index 4b8a0c2..f7bbf98 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
@@ -29,6 +29,7 @@
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
+import com.android.systemui.statusbar.notification.collection.EntryAdapter
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiForceExpanded
@@ -436,6 +437,9 @@
val row = mock(ExpandableNotificationRow::class.java)
val entry = mock(NotificationEntry::class.java)
whenever(entry.isStickyAndNotDemoted).thenReturn(isSticky)
+ val entryAdapter = mock(EntryAdapter::class.java)
+ whenever(entryAdapter.canPeek()).thenReturn(isSticky)
+ whenever(row.entryAdapter).thenReturn(entryAdapter)
val sbn = mock(StatusBarNotification::class.java)
whenever(entry.sbn).thenReturn(sbn)
whenever(row.entry).thenReturn(entry)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index ce655ef..9545150 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -27,6 +27,8 @@
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView.FooterViewState
import com.android.systemui.statusbar.notification.headsup.AvalancheController
+import com.android.systemui.statusbar.notification.headsup.HeadsUpAnimator
+import com.android.systemui.statusbar.notification.headsup.NotificationsHunSharedAnimationValues
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
@@ -55,7 +57,8 @@
private val avalancheController = mock<AvalancheController>()
private val hostView = FrameLayout(context)
- private val stackScrollAlgorithm = StackScrollAlgorithm(context, hostView)
+ private lateinit var headsUpAnimator: HeadsUpAnimator
+ private lateinit var stackScrollAlgorithm: StackScrollAlgorithm
private val notificationRow = mock<ExpandableNotificationRow>()
private val notificationEntry = mock<NotificationEntry>()
private val notificationEntryAdapter = mock<EntryAdapter>()
@@ -101,7 +104,10 @@
@JvmStatic
@Parameters(name = "{0}")
fun getParams(): List<FlagsParameterization> {
- return FlagsParameterization.allCombinationsOf().andSceneContainer()
+ return FlagsParameterization.allCombinationsOf(
+ NotificationsHunSharedAnimationValues.FLAG_NAME
+ )
+ .andSceneContainer()
}
}
@@ -123,6 +129,15 @@
ambientState.isSmallScreen = true
hostView.addView(notificationRow)
+
+ if (NotificationsHunSharedAnimationValues.isEnabled) {
+ headsUpAnimator = HeadsUpAnimator(context)
+ }
+ stackScrollAlgorithm = StackScrollAlgorithm(
+ context,
+ hostView,
+ if (::headsUpAnimator.isInitialized) headsUpAnimator else null,
+ )
}
private fun isTv(): Boolean {
@@ -403,7 +418,11 @@
ambientState.setLayoutMinHeight(2500) // Mock the height of shade
ambientState.stackY = 2500f // Scroll over the max translation
stackScrollAlgorithm.setIsExpanded(true) // Mark the shade open
- stackScrollAlgorithm.setHeadsUpAppearHeightBottom(bottomOfScreen.toInt())
+ if (NotificationsHunSharedAnimationValues.isEnabled) {
+ headsUpAnimator.headsUpAppearHeightBottom = bottomOfScreen.toInt()
+ } else {
+ stackScrollAlgorithm.setHeadsUpAppearHeightBottom(bottomOfScreen.toInt())
+ }
whenever(notificationRow.mustStayOnScreen()).thenReturn(true)
whenever(notificationRow.isHeadsUp).thenReturn(true)
whenever(notificationRow.isAboveShelf).thenReturn(true)
@@ -419,6 +438,9 @@
val topMargin = 100f
ambientState.maxHeadsUpTranslation = 2000f
ambientState.stackTopMargin = topMargin.toInt()
+ if (NotificationsHunSharedAnimationValues.isEnabled) {
+ headsUpAnimator.stackTopMargin = topMargin.toInt()
+ }
whenever(notificationRow.intrinsicHeight).thenReturn(100)
whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
index e7be20e..cb4642c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
@@ -16,12 +16,18 @@
package com.android.systemui.statusbar.notification.stack
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
import android.testing.TestableLooper.RunWithLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.AnimatorTestRule
import com.android.systemui.res.R
+import com.android.systemui.statusbar.notification.headsup.HeadsUpAnimator
+import com.android.systemui.statusbar.notification.headsup.NotificationsHunSharedAnimationValues
import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent
import com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_HEADS_UP_APPEAR
@@ -40,6 +46,7 @@
import org.mockito.Mockito.description
import org.mockito.Mockito.eq
import org.mockito.Mockito.verify
+import org.mockito.kotlin.doNothing
private const val VIEW_HEIGHT = 100
private const val FULL_SHADE_APPEAR_TRANSLATION = 300
@@ -50,17 +57,19 @@
@RunWithLooper
class StackStateAnimatorTest : SysuiTestCase() {
+ @get:Rule val setFlagsRule = SetFlagsRule()
@get:Rule val animatorTestRule = AnimatorTestRule(this)
private lateinit var stackStateAnimator: StackStateAnimator
+ private lateinit var headsUpAnimator: HeadsUpAnimator
private val stackScroller: NotificationStackScrollLayout = mock()
private val view: ExpandableView = mock()
- private val viewState: ExpandableViewState =
- ExpandableViewState().apply { height = VIEW_HEIGHT }
+ private lateinit var viewState: ExpandableViewState
private val runnableCaptor: ArgumentCaptor<Runnable> = argumentCaptor()
@Before
fun setUp() {
+ viewState = ExpandableViewState().apply { height = VIEW_HEIGHT }
overrideResource(
R.dimen.go_to_full_shade_appearing_translation,
FULL_SHADE_APPEAR_TRANSLATION,
@@ -69,11 +78,20 @@
whenever(stackScroller.context).thenReturn(context)
whenever(view.viewState).thenReturn(viewState)
- stackStateAnimator = StackStateAnimator(mContext, stackScroller)
+
+ if (NotificationsHunSharedAnimationValues.isEnabled) {
+ headsUpAnimator = HeadsUpAnimator(context)
+ }
+ stackStateAnimator = StackStateAnimator(
+ mContext,
+ stackScroller,
+ if (::headsUpAnimator.isInitialized) headsUpAnimator else null,
+ )
}
@Test
- fun startAnimationForEvents_headsUpFromTop_startsHeadsUpAppearAnim() {
+ @DisableFlags(NotificationsHunSharedAnimationValues.FLAG_NAME)
+ fun startAnimationForEvents_headsUpFromTop_startsHeadsUpAppearAnim_flagOff() {
val topMargin = 50f
val expectedStartY = -topMargin - stackStateAnimator.mHeadsUpAppearStartAboveScreen
val event = AnimationEvent(view, AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR)
@@ -94,7 +112,30 @@
}
@Test
- fun startAnimationForEvents_headsUpFromBottom_startsHeadsUpAppearAnim() {
+ @EnableFlags(NotificationsHunSharedAnimationValues.FLAG_NAME)
+ fun startAnimationForEvents_headsUpFromTop_startsHeadsUpAppearAnim_flagOn() {
+ val topMargin = 50f
+ val expectedStartY = -topMargin - HEADS_UP_ABOVE_SCREEN
+ val event = AnimationEvent(view, AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR)
+ headsUpAnimator.stackTopMargin = topMargin.toInt()
+
+ stackStateAnimator.startAnimationForEvents(arrayListOf(event), 0)
+
+ verify(view).setFinalActualHeight(VIEW_HEIGHT)
+ verify(view, description("should animate from the top")).translationY = expectedStartY
+ verify(view)
+ .performAddAnimation(
+ /* delay= */ 0L,
+ /* duration= */ ANIMATION_DURATION_HEADS_UP_APPEAR.toLong(),
+ /* isHeadsUpAppear= */ true,
+ /* isHeadsUpCycling= */ false,
+ /* onEndRunnable= */ null,
+ )
+ }
+
+ @Test
+ @DisableFlags(NotificationsHunSharedAnimationValues.FLAG_NAME)
+ fun startAnimationForEvents_headsUpFromBottom_startsHeadsUpAppearAnim_flagOff() {
val screenHeight = 2000f
val expectedStartY = screenHeight + stackStateAnimator.mHeadsUpAppearStartAboveScreen
val event =
@@ -117,8 +158,35 @@
)
}
+ @DisableFlags(Flags.FLAG_PHYSICAL_NOTIFICATION_MOVEMENT)
@Test
- fun startAnimationForEvents_startsHeadsUpDisappearAnim() {
+ @EnableFlags(NotificationsHunSharedAnimationValues.FLAG_NAME)
+ fun startAnimationForEvents_headsUpFromBottom_startsHeadsUpAppearAnim_flagOn() {
+ val screenHeight = 2000f
+ val expectedStartY = screenHeight + HEADS_UP_ABOVE_SCREEN
+ val event =
+ AnimationEvent(view, AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR).apply {
+ headsUpFromBottom = true
+ }
+ headsUpAnimator.headsUpAppearHeightBottom = screenHeight.toInt()
+
+ stackStateAnimator.startAnimationForEvents(arrayListOf(event), 0)
+
+ verify(view).setFinalActualHeight(VIEW_HEIGHT)
+ verify(view, description("should animate from the bottom")).translationY = expectedStartY
+ verify(view)
+ .performAddAnimation(
+ /* delay= */ 0L,
+ /* duration= */ ANIMATION_DURATION_HEADS_UP_APPEAR.toLong(),
+ /* isHeadsUpAppear= */ true,
+ /* isHeadsUpCycling= */ false,
+ /* onEndRunnable= */ null,
+ )
+ }
+
+ @Test
+ @DisableFlags(NotificationsHunSharedAnimationValues.FLAG_NAME)
+ fun startAnimationForEvents_startsHeadsUpDisappearAnim_flagOff() {
val disappearDuration = ANIMATION_DURATION_HEADS_UP_DISAPPEAR.toLong()
val event = AnimationEvent(view, AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR)
clearInvocations(view)
@@ -147,6 +215,62 @@
}
@Test
+ @EnableFlags(NotificationsHunSharedAnimationValues.FLAG_NAME)
+ fun startAnimationForEvents_startsHeadsUpDisappearAnim_flagOn() {
+ val disappearDuration = ANIMATION_DURATION_HEADS_UP_DISAPPEAR.toLong()
+ val event = AnimationEvent(view, AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR)
+ clearInvocations(view)
+ stackStateAnimator.startAnimationForEvents(arrayListOf(event), 0)
+
+ verify(view)
+ .performRemoveAnimation(
+ /* duration= */ eq(disappearDuration),
+ /* delay= */ eq(0L),
+ /* translationDirection= */ eq(0f),
+ /* isHeadsUpAnimation= */ eq(true),
+ /* isHeadsUpCycling= */ eq(false),
+ /* onStartedRunnable= */ any(),
+ /* onFinishedRunnable= */ runnableCaptor.capture(),
+ /* animationListener= */ any(),
+ /* clipSide= */ eq(ExpandableView.ClipSide.BOTTOM),
+ )
+
+ animatorTestRule.advanceTimeBy(disappearDuration) // move to the end of SSA animations
+ runnableCaptor.value.run() // execute the end runnable
+
+ verify(view, description("should be translated to the heads up appear start"))
+ .translationY = -stackStateAnimator.mHeadsUpAppearStartAboveScreen
+ verify(view, description("should be called at the end of the disappear animation"))
+ .removeFromTransientContainer()
+ }
+
+ @EnableFlags(Flags.FLAG_PHYSICAL_NOTIFICATION_MOVEMENT)
+ @Test
+ fun startAnimationForEvents_startsHeadsUpDisappearAnim_physical() {
+ val disappearDuration = ANIMATION_DURATION_HEADS_UP_DISAPPEAR.toLong()
+ val event = AnimationEvent(view, AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR)
+ clearInvocations(view)
+ stackStateAnimator.startAnimationForEvents(arrayListOf(event), 0)
+
+ verify(view)
+ .performRemoveAnimation(
+ /* duration= */ eq(disappearDuration),
+ /* delay= */ eq(0L),
+ /* translationDirection= */ eq(0f),
+ /* isHeadsUpAnimation= */ eq(true),
+ /* isHeadsUpCycling= */ eq(false),
+ /* onStartedRunnable= */ any(),
+ /* onFinishedRunnable= */ runnableCaptor.capture(),
+ /* animationListener= */ any(),
+ /* clipSide= */ eq(ExpandableView.ClipSide.BOTTOM),
+ )
+
+ runnableCaptor.value.run() // execute the end runnable
+ verify(view, description("should be called at the end of the disappear animation"))
+ .removeFromTransientContainer()
+ }
+
+ @Test
fun initView_updatesResources() {
// Given: the resource values are initialized in the SSA
assertThat(stackStateAnimator.mGoToFullShadeAppearingTranslation)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt
index ef415c9..6ee1c4d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.stack
+import android.animation.AnimatorTestRule
import android.animation.ValueAnimator
import android.view.View
import androidx.test.annotation.UiThreadTest
@@ -28,6 +29,7 @@
import com.android.systemui.statusbar.notification.PhysicsPropertyAnimator.Companion.TAG_ANIMATOR_TRANSLATION_Y
import com.android.systemui.statusbar.notification.PhysicsPropertyAnimator.Companion.Y_TRANSLATION
import org.junit.Assert
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import kotlin.math.log2
@@ -38,6 +40,8 @@
@UiThreadTest
class ViewStateTest : SysuiTestCase() {
private val viewState = ViewState(true /* usePhysicsForMovement */)
+ @get:Rule
+ val animatorTestRule = AnimatorTestRule(this)
@Suppress("DIVISION_BY_ZERO")
@Test
diff --git a/packages/SystemUI/pods/Android.bp b/packages/SystemUI/pods/Android.bp
index 1547ae2..fba8962 100644
--- a/packages/SystemUI/pods/Android.bp
+++ b/packages/SystemUI/pods/Android.bp
@@ -31,7 +31,7 @@
warning_checks: ["MissingApacheLicenseDetector"],
},
kotlincflags: [
- "-Xexplicit-api=warning",
+ "-Xexplicit-api=strict",
"-Xjvm-default=all",
],
defaults_visibility: [":__subpackages__"],
diff --git a/packages/SystemUI/pods/com/android/systemui/retail/RetailModeModule.kt b/packages/SystemUI/pods/com/android/systemui/retail/RetailModeModule.kt
index c20e368..fe78bb3 100644
--- a/packages/SystemUI/pods/com/android/systemui/retail/RetailModeModule.kt
+++ b/packages/SystemUI/pods/com/android/systemui/retail/RetailModeModule.kt
@@ -24,11 +24,15 @@
import dagger.Module
@Module
-abstract class RetailModeModule {
+public abstract class RetailModeModule {
@Binds
- abstract fun bindsRetailModeRepository(impl: RetailModeSettingsRepository): RetailModeRepository
+ public abstract fun bindsRetailModeRepository(
+ impl: RetailModeSettingsRepository
+ ): RetailModeRepository
@Binds
- abstract fun bindsRetailModeInteractor(impl: RetailModeInteractorImpl): RetailModeInteractor
+ public abstract fun bindsRetailModeInteractor(
+ impl: RetailModeInteractorImpl
+ ): RetailModeInteractor
}
diff --git a/packages/SystemUI/pods/com/android/systemui/retail/data/repository/RetailModeRepository.kt b/packages/SystemUI/pods/com/android/systemui/retail/data/repository/RetailModeRepository.kt
index c9eac25..4fd6985 100644
--- a/packages/SystemUI/pods/com/android/systemui/retail/data/repository/RetailModeRepository.kt
+++ b/packages/SystemUI/pods/com/android/systemui/retail/data/repository/RetailModeRepository.kt
@@ -19,11 +19,11 @@
import kotlinx.coroutines.flow.StateFlow
/** Repository to track if the device is in Retail mode */
-interface RetailModeRepository {
+public interface RetailModeRepository {
/** Flow of whether the device is currently in retail mode. */
- val retailMode: StateFlow<Boolean>
+ public val retailMode: StateFlow<Boolean>
/** Last value of whether the device is in retail mode. */
- val inRetailMode: Boolean
+ public val inRetailMode: Boolean
get() = retailMode.value
}
diff --git a/packages/SystemUI/pods/com/android/systemui/retail/data/repository/impl/RetailModeSettingsRepository.kt b/packages/SystemUI/pods/com/android/systemui/retail/data/repository/impl/RetailModeSettingsRepository.kt
index 8955263..7296835 100644
--- a/packages/SystemUI/pods/com/android/systemui/retail/data/repository/impl/RetailModeSettingsRepository.kt
+++ b/packages/SystemUI/pods/com/android/systemui/retail/data/repository/impl/RetailModeSettingsRepository.kt
@@ -38,17 +38,17 @@
/**
* Tracks [Settings.Global.DEVICE_DEMO_MODE].
*
- * @see UserManager.isDeviceInDemoMode
+ * @see android.os.UserManager.isDeviceInDemoMode
*/
@SysUISingleton
-class RetailModeSettingsRepository
+public class RetailModeSettingsRepository
@Inject
constructor(
globalSettings: GlobalSettings,
@Background backgroundDispatcher: CoroutineDispatcher,
@Application scope: CoroutineScope,
) : RetailModeRepository {
- override val retailMode =
+ override val retailMode: StateFlow<Boolean> =
conflatedCallbackFlow {
val observer =
object : ContentObserver(null) {
@@ -66,7 +66,7 @@
.flowOn(backgroundDispatcher)
.stateIn(scope, SharingStarted.Eagerly, false)
- companion object {
+ public companion object {
private const val RETAIL_MODE_SETTING = Settings.Global.DEVICE_DEMO_MODE
}
}
diff --git a/packages/SystemUI/pods/com/android/systemui/retail/domain/interactor/RetailModeInteractor.kt b/packages/SystemUI/pods/com/android/systemui/retail/domain/interactor/RetailModeInteractor.kt
index 748e34d..3e44f8d 100644
--- a/packages/SystemUI/pods/com/android/systemui/retail/domain/interactor/RetailModeInteractor.kt
+++ b/packages/SystemUI/pods/com/android/systemui/retail/domain/interactor/RetailModeInteractor.kt
@@ -17,7 +17,7 @@
package com.android.systemui.retail.domain.interactor
/** Interactor to determine if the device is currently in retail mode */
-interface RetailModeInteractor {
+public interface RetailModeInteractor {
/** Whether the device is currently in retail mode */
- val isInRetailMode: Boolean
+ public val isInRetailMode: Boolean
}
diff --git a/packages/SystemUI/pods/com/android/systemui/retail/domain/interactor/impl/RetailModeInteractorImpl.kt b/packages/SystemUI/pods/com/android/systemui/retail/domain/interactor/impl/RetailModeInteractorImpl.kt
index 8dbe562..52b4bcc 100644
--- a/packages/SystemUI/pods/com/android/systemui/retail/domain/interactor/impl/RetailModeInteractorImpl.kt
+++ b/packages/SystemUI/pods/com/android/systemui/retail/domain/interactor/impl/RetailModeInteractorImpl.kt
@@ -22,11 +22,9 @@
import javax.inject.Inject
@SysUISingleton
-class RetailModeInteractorImpl
+public class RetailModeInteractorImpl
@Inject
-constructor(
- private val repository: RetailModeRepository,
-) : RetailModeInteractor {
+constructor(private val repository: RetailModeRepository) : RetailModeInteractor {
override val isInRetailMode: Boolean
get() = repository.inRetailMode
}
diff --git a/packages/SystemUI/pods/com/android/systemui/util/settings/SettingsProxy.kt b/packages/SystemUI/pods/com/android/systemui/util/settings/SettingsProxy.kt
index 597276a..a8d4f79 100644
--- a/packages/SystemUI/pods/com/android/systemui/util/settings/SettingsProxy.kt
+++ b/packages/SystemUI/pods/com/android/systemui/util/settings/SettingsProxy.kt
@@ -19,6 +19,7 @@
import android.content.ContentResolver
import android.database.ContentObserver
import android.net.Uri
+import android.provider.Settings
import android.provider.Settings.SettingNotFoundException
import androidx.annotation.AnyThread
import androidx.annotation.WorkerThread
@@ -28,6 +29,7 @@
import kotlin.coroutines.coroutineContext
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
/**
* Used to interact with mainly with Settings.Global, but can also be used for Settings.System and
@@ -43,15 +45,15 @@
* This class also provides [.registerContentObserver] methods, normally found on [ContentResolver]
* instances, unifying setting related actions in one place.
*/
-interface SettingsProxy {
+public interface SettingsProxy {
/** Returns the [ContentResolver] this instance was constructed with. */
- fun getContentResolver(): ContentResolver
+ public fun getContentResolver(): ContentResolver
/** Returns the [CoroutineScope] that the async APIs will use. */
- val settingsScope: CoroutineScope
+ public val settingsScope: CoroutineScope
@OptIn(ExperimentalStdlibApi::class)
- suspend fun executeOnSettingsScopeDispatcher(name: String, block: () -> Unit) {
+ public suspend fun executeOnSettingsScopeDispatcher(name: String, block: () -> Unit) {
val settingsDispatcher = settingsScope.coroutineContext[CoroutineDispatcher]
if (
settingsDispatcher != null &&
@@ -70,7 +72,7 @@
* @param name to look up in the table
* @return the corresponding content URI, or null if not present
*/
- @AnyThread fun getUriFor(name: String): Uri
+ @AnyThread public fun getUriFor(name: String): Uri
/**
* Registers listener for a given content observer <b>while blocking the current thread</b>.
@@ -80,7 +82,7 @@
* [registerContentObserverAsync] instead.
*/
@WorkerThread
- fun registerContentObserverSync(name: String, settingsObserver: ContentObserver) {
+ public fun registerContentObserverSync(name: String, settingsObserver: ContentObserver) {
registerContentObserverSync(getUriFor(name), settingsObserver)
}
@@ -91,7 +93,7 @@
* registration happens on a worker thread. Caller may wrap the API in an async block if they
* wish to synchronize execution.
*/
- suspend fun registerContentObserver(name: String, settingsObserver: ContentObserver) {
+ public suspend fun registerContentObserver(name: String, settingsObserver: ContentObserver) {
executeOnSettingsScopeDispatcher("registerContentObserver-A") {
registerContentObserverSync(getUriFor(name), settingsObserver)
}
@@ -103,7 +105,7 @@
* API corresponding to [registerContentObserver] for Java usage.
*/
@AnyThread
- fun registerContentObserverAsync(name: String, settingsObserver: ContentObserver) =
+ public fun registerContentObserverAsync(name: String, settingsObserver: ContentObserver): Job =
settingsScope.launch("registerContentObserverAsync-A") {
registerContentObserverSync(getUriFor(name), settingsObserver)
}
@@ -116,11 +118,11 @@
* value.
*/
@AnyThread
- fun registerContentObserverAsync(
+ public fun registerContentObserverAsync(
name: String,
settingsObserver: ContentObserver,
@WorkerThread registered: Runnable,
- ) =
+ ): Job =
settingsScope.launch("registerContentObserverAsync-B") {
registerContentObserverSync(getUriFor(name), settingsObserver)
registered.run()
@@ -133,8 +135,9 @@
* [registerContentObserverAsync] instead.
*/
@WorkerThread
- fun registerContentObserverSync(uri: Uri, settingsObserver: ContentObserver) =
+ public fun registerContentObserverSync(uri: Uri, settingsObserver: ContentObserver) {
registerContentObserverSync(uri, false, settingsObserver)
+ }
/**
* Convenience wrapper around [ContentResolver.registerContentObserver].'
@@ -143,7 +146,7 @@
* registration happens on a worker thread. Caller may wrap the API in an async block if they
* wish to synchronize execution.
*/
- suspend fun registerContentObserver(uri: Uri, settingsObserver: ContentObserver) {
+ public suspend fun registerContentObserver(uri: Uri, settingsObserver: ContentObserver) {
executeOnSettingsScopeDispatcher("registerContentObserver-B") {
registerContentObserverSync(uri, settingsObserver)
}
@@ -155,7 +158,7 @@
* API corresponding to [registerContentObserver] for Java usage.
*/
@AnyThread
- fun registerContentObserverAsync(uri: Uri, settingsObserver: ContentObserver) =
+ public fun registerContentObserverAsync(uri: Uri, settingsObserver: ContentObserver): Job =
settingsScope.launch("registerContentObserverAsync-C") {
registerContentObserverSync(uri, settingsObserver)
}
@@ -168,11 +171,11 @@
* value.
*/
@AnyThread
- fun registerContentObserverAsync(
+ public fun registerContentObserverAsync(
uri: Uri,
settingsObserver: ContentObserver,
@WorkerThread registered: Runnable,
- ) =
+ ): Job =
settingsScope.launch("registerContentObserverAsync-D") {
registerContentObserverSync(uri, settingsObserver)
registered.run()
@@ -184,11 +187,13 @@
* Implicitly calls [getUriFor] on the passed in name.
*/
@WorkerThread
- fun registerContentObserverSync(
+ public fun registerContentObserverSync(
name: String,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
- ) = registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver)
+ ) {
+ registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver)
+ }
/**
* Convenience wrapper around [ContentResolver.registerContentObserver].'
@@ -197,7 +202,7 @@
* registration happens on a worker thread. Caller may wrap the API in an async block if they
* wish to synchronize execution.
*/
- suspend fun registerContentObserver(
+ public suspend fun registerContentObserver(
name: String,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
@@ -213,11 +218,11 @@
* API corresponding to [registerContentObserver] for Java usage.
*/
@AnyThread
- fun registerContentObserverAsync(
+ public fun registerContentObserverAsync(
name: String,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
- ) =
+ ): Job =
settingsScope.launch("registerContentObserverAsync-E") {
registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver)
}
@@ -230,12 +235,12 @@
* value.
*/
@AnyThread
- fun registerContentObserverAsync(
+ public fun registerContentObserverAsync(
name: String,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
@WorkerThread registered: Runnable,
- ) =
+ ): Job =
settingsScope.launch("registerContentObserverAsync-F") {
registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver)
registered.run()
@@ -248,7 +253,7 @@
* [registerContentObserverAsync] instead.
*/
@WorkerThread
- fun registerContentObserverSync(
+ public fun registerContentObserverSync(
uri: Uri,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
@@ -266,7 +271,7 @@
* registration happens on a worker thread. Caller may wrap the API in an async block if they
* wish to synchronize execution.
*/
- suspend fun registerContentObserver(
+ public suspend fun registerContentObserver(
uri: Uri,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
@@ -282,11 +287,11 @@
* API corresponding to [registerContentObserver] for Java usage.
*/
@AnyThread
- fun registerContentObserverAsync(
+ public fun registerContentObserverAsync(
uri: Uri,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
- ) =
+ ): Job =
settingsScope.launch("registerContentObserverAsync-G") {
registerContentObserverSync(uri, notifyForDescendants, settingsObserver)
}
@@ -299,12 +304,12 @@
* value.
*/
@AnyThread
- fun registerContentObserverAsync(
+ public fun registerContentObserverAsync(
uri: Uri,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
@WorkerThread registered: Runnable,
- ) =
+ ): Job =
settingsScope.launch("registerContentObserverAsync-H") {
registerContentObserverSync(uri, notifyForDescendants, settingsObserver)
registered.run()
@@ -317,7 +322,7 @@
* [unregisterContentObserverAsync] instead.
*/
@WorkerThread
- fun unregisterContentObserverSync(settingsObserver: ContentObserver) {
+ public fun unregisterContentObserverSync(settingsObserver: ContentObserver) {
trace({ "SP#unregisterObserver" }) {
getContentResolver().unregisterContentObserver(settingsObserver)
}
@@ -330,7 +335,7 @@
* [ContentObserver] un-registration happens on a worker thread. Caller may wrap the API in an
* async block if they wish to synchronize execution.
*/
- suspend fun unregisterContentObserver(settingsObserver: ContentObserver) {
+ public suspend fun unregisterContentObserver(settingsObserver: ContentObserver) {
executeOnSettingsScopeDispatcher("unregisterContentObserver") {
unregisterContentObserverSync(settingsObserver)
}
@@ -343,7 +348,7 @@
* [ContentObserver] registration happens on a worker thread.
*/
@AnyThread
- fun unregisterContentObserverAsync(settingsObserver: ContentObserver) =
+ public fun unregisterContentObserverAsync(settingsObserver: ContentObserver): Job =
settingsScope.launch("unregisterContentObserverAsync") {
unregisterContentObserver(settingsObserver)
}
@@ -354,7 +359,7 @@
* @param name to look up in the table
* @return the corresponding value, or null if not present
*/
- fun getString(name: String): String?
+ public fun getString(name: String): String?
/**
* Store a name/value pair into the database.
@@ -363,7 +368,7 @@
* @param value to associate with the name
* @return true if the value was set, false on database errors
*/
- fun putString(name: String, value: String?): Boolean
+ public fun putString(name: String, value: String?): Boolean
/**
* Store a name/value pair into the database.
@@ -394,7 +399,7 @@
* @return true if the value was set, false on database errors.
* @see .resetToDefaults
*/
- fun putString(name: String, value: String?, tag: String?, makeDefault: Boolean): Boolean
+ public fun putString(name: String, value: String?, tag: String?, makeDefault: Boolean): Boolean
/**
* Convenience function for retrieving a single secure settings value as an integer. Note that
@@ -406,7 +411,7 @@
* @param default Value to return if the setting is not defined.
* @return The setting's current value, or default if it is not defined or not a valid integer.
*/
- fun getInt(name: String, default: Int): Int {
+ public fun getInt(name: String, default: Int): Int {
val v = getString(name)
return try {
v?.toInt() ?: default
@@ -429,7 +434,7 @@
* found or the setting value is not an integer.
*/
@Throws(SettingNotFoundException::class)
- fun getInt(name: String): Int {
+ public fun getInt(name: String): Int {
val v = getString(name) ?: throw SettingNotFoundException(name)
return try {
v.toInt()
@@ -448,7 +453,7 @@
* @param value The new value for the setting.
* @return true if the value was set, false on database errors
*/
- fun putInt(name: String, value: Int): Boolean {
+ public fun putInt(name: String, value: Int): Boolean {
return putString(name, value.toString())
}
@@ -462,7 +467,7 @@
* @param default Value to return if the setting is not defined.
* @return The setting's current value, or default if it is not defined or not a valid boolean.
*/
- fun getBool(name: String, default: Boolean): Boolean {
+ public fun getBool(name: String, default: Boolean): Boolean {
return getInt(name, if (default) 1 else 0) != 0
}
@@ -480,7 +485,7 @@
* found or the setting value is not a boolean.
*/
@Throws(SettingNotFoundException::class)
- fun getBool(name: String): Boolean {
+ public fun getBool(name: String): Boolean {
return getInt(name) != 0
}
@@ -494,7 +499,7 @@
* @param value The new value for the setting.
* @return true if the value was set, false on database errors
*/
- fun putBool(name: String, value: Boolean): Boolean {
+ public fun putBool(name: String, value: Boolean): Boolean {
return putInt(name, if (value) 1 else 0)
}
@@ -508,7 +513,7 @@
* @param def Value to return if the setting is not defined.
* @return The setting's current value, or 'def' if it is not defined or not a valid `long`.
*/
- fun getLong(name: String, def: Long): Long {
+ public fun getLong(name: String, def: Long): Long {
val valString = getString(name)
return parseLongOrUseDefault(valString, def)
}
@@ -527,7 +532,7 @@
* found or the setting value is not an integer.
*/
@Throws(SettingNotFoundException::class)
- fun getLong(name: String): Long {
+ public fun getLong(name: String): Long {
val valString = getString(name)
return parseLongOrThrow(name, valString)
}
@@ -542,7 +547,7 @@
* @param value The new value for the setting.
* @return true if the value was set, false on database errors
*/
- fun putLong(name: String, value: Long): Boolean {
+ public fun putLong(name: String, value: Long): Boolean {
return putString(name, value.toString())
}
@@ -556,7 +561,7 @@
* @param def Value to return if the setting is not defined.
* @return The setting's current value, or 'def' if it is not defined or not a valid float.
*/
- fun getFloat(name: String, def: Float): Float {
+ public fun getFloat(name: String, def: Float): Float {
val v = getString(name)
return parseFloat(v, def)
}
@@ -575,7 +580,7 @@
* found or the setting value is not a float.
*/
@Throws(SettingNotFoundException::class)
- fun getFloat(name: String): Float {
+ public fun getFloat(name: String): Float {
val v = getString(name)
return parseFloatOrThrow(name, v)
}
@@ -590,14 +595,14 @@
* @param value The new value for the setting.
* @return true if the value was set, false on database errors
*/
- fun putFloat(name: String, value: Float): Boolean {
+ public fun putFloat(name: String, value: Float): Boolean {
return putString(name, value.toString())
}
- companion object {
+ public companion object {
/** Convert a string to a long, or uses a default if the string is malformed or null */
@JvmStatic
- fun parseLongOrUseDefault(valString: String?, default: Long): Long {
+ public fun parseLongOrUseDefault(valString: String?, default: Long): Long {
val value: Long =
try {
valString?.toLong() ?: default
@@ -610,7 +615,7 @@
/** Convert a string to a long, or throws an exception if the string is malformed or null */
@JvmStatic
@Throws(SettingNotFoundException::class)
- fun parseLongOrThrow(name: String, valString: String?): Long {
+ public fun parseLongOrThrow(name: String, valString: String?): Long {
if (valString == null) {
throw SettingNotFoundException(name)
}
@@ -623,7 +628,7 @@
/** Convert a string to a float, or uses a default if the string is malformed or null */
@JvmStatic
- fun parseFloat(v: String?, def: Float): Float {
+ public fun parseFloat(v: String?, def: Float): Float {
return try {
v?.toFloat() ?: def
} catch (e: NumberFormatException) {
@@ -636,7 +641,7 @@
*/
@JvmStatic
@Throws(SettingNotFoundException::class)
- fun parseFloatOrThrow(name: String, v: String?): Float {
+ public fun parseFloatOrThrow(name: String, v: String?): Float {
if (v == null) {
throw SettingNotFoundException(name)
}
@@ -648,7 +653,7 @@
}
}
- fun interface CurrentUserIdProvider {
- @UserIdInt fun getUserId(): Int
+ public fun interface CurrentUserIdProvider {
+ @UserIdInt public fun getUserId(): Int
}
}
diff --git a/packages/SystemUI/pods/com/android/systemui/util/settings/SettingsProxyExt.kt b/packages/SystemUI/pods/com/android/systemui/util/settings/SettingsProxyExt.kt
index 36468144..60feaf1 100644
--- a/packages/SystemUI/pods/com/android/systemui/util/settings/SettingsProxyExt.kt
+++ b/packages/SystemUI/pods/com/android/systemui/util/settings/SettingsProxyExt.kt
@@ -17,6 +17,7 @@
package com.android.systemui.util.settings
+import android.annotation.SuppressLint
import android.annotation.UserIdInt
import android.database.ContentObserver
import com.android.systemui.Flags
@@ -25,10 +26,11 @@
import kotlinx.coroutines.flow.Flow
/** Kotlin extension functions for [SettingsProxy]. */
-object SettingsProxyExt {
+@SuppressLint("RegisterContentObserverSyncWarning")
+public object SettingsProxyExt {
/** Returns a flow of [Unit] that is invoked each time that content is updated. */
- fun UserSettingsProxy.observerFlow(
+ public fun UserSettingsProxy.observerFlow(
@UserIdInt userId: Int,
vararg names: String,
): Flow<Unit> {
@@ -59,9 +61,7 @@
}
/** Returns a flow of [Unit] that is invoked each time that content is updated. */
- fun SettingsProxy.observerFlow(
- vararg names: String,
- ): Flow<Unit> {
+ public fun SettingsProxy.observerFlow(vararg names: String): Flow<Unit> {
return conflatedCallbackFlow {
val observer =
object : ContentObserver(null) {
diff --git a/packages/SystemUI/pods/com/android/systemui/util/settings/UserSettingsProxy.kt b/packages/SystemUI/pods/com/android/systemui/util/settings/UserSettingsProxy.kt
index 3ccac9e3..61c7f73 100644
--- a/packages/SystemUI/pods/com/android/systemui/util/settings/UserSettingsProxy.kt
+++ b/packages/SystemUI/pods/com/android/systemui/util/settings/UserSettingsProxy.kt
@@ -15,6 +15,7 @@
*/
package com.android.systemui.util.settings
+import android.annotation.SuppressLint
import android.annotation.UserIdInt
import android.annotation.WorkerThread
import android.content.ContentResolver
@@ -28,6 +29,7 @@
import com.android.systemui.util.settings.SettingsProxy.Companion.parseFloatOrThrow
import com.android.systemui.util.settings.SettingsProxy.Companion.parseLongOrThrow
import com.android.systemui.util.settings.SettingsProxy.Companion.parseLongOrUseDefault
+import kotlinx.coroutines.Job
/**
* Used to interact with per-user Settings.Secure and Settings.System settings (but not
@@ -42,11 +44,12 @@
* This class also provides [.registerContentObserver] methods, normally found on [ContentResolver]
* instances, unifying setting related actions in one place.
*/
-interface UserSettingsProxy : SettingsProxy {
- val currentUserProvider: SettingsProxy.CurrentUserIdProvider
+@SuppressLint("RegisterContentObserverSyncWarning")
+public interface UserSettingsProxy : SettingsProxy {
+ public val currentUserProvider: SettingsProxy.CurrentUserIdProvider
/** Returns the user id for the associated [ContentResolver]. */
- var userId: Int
+ public var userId: Int
get() = getContentResolver().userId
set(_) {
throw UnsupportedOperationException(
@@ -58,7 +61,7 @@
* Returns the actual current user handle when querying with the current user. Otherwise,
* returns the passed in user id.
*/
- fun getRealUserHandle(userHandle: Int): Int {
+ public fun getRealUserHandle(userHandle: Int): Int {
return if (userHandle != UserHandle.USER_CURRENT) {
userHandle
} else currentUserProvider.getUserId()
@@ -75,7 +78,7 @@
}
}
- override fun registerContentObserverAsync(uri: Uri, settingsObserver: ContentObserver) =
+ override fun registerContentObserverAsync(uri: Uri, settingsObserver: ContentObserver): Job =
settingsScope.launch("registerContentObserverAsync-A") {
registerContentObserverForUserSync(uri, settingsObserver, userId)
}
@@ -109,7 +112,7 @@
uri: Uri,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
- ) =
+ ): Job =
settingsScope.launch("registerContentObserverAsync-B") {
registerContentObserverForUserSync(uri, notifyForDescendants, settingsObserver, userId)
}
@@ -120,7 +123,7 @@
* Implicitly calls [getUriFor] on the passed in name.
*/
@WorkerThread
- fun registerContentObserverForUserSync(
+ public fun registerContentObserverForUserSync(
name: String,
settingsObserver: ContentObserver,
userHandle: Int,
@@ -135,7 +138,7 @@
* [ContentObserver] registration happens on a worker thread. Caller may wrap the API in an
* async block if they wish to synchronize execution.
*/
- suspend fun registerContentObserverForUser(
+ public suspend fun registerContentObserverForUser(
name: String,
settingsObserver: ContentObserver,
userHandle: Int,
@@ -150,11 +153,11 @@
*
* API corresponding to [registerContentObserverForUser] for Java usage.
*/
- fun registerContentObserverForUserAsync(
+ public fun registerContentObserverForUserAsync(
name: String,
settingsObserver: ContentObserver,
userHandle: Int,
- ) =
+ ): Job =
settingsScope.launch("registerContentObserverForUserAsync-A") {
try {
registerContentObserverForUserSync(getUriFor(name), settingsObserver, userHandle)
@@ -165,7 +168,7 @@
/** Convenience wrapper around [ContentResolver.registerContentObserver] */
@WorkerThread
- fun registerContentObserverForUserSync(
+ public fun registerContentObserverForUserSync(
uri: Uri,
settingsObserver: ContentObserver,
userHandle: Int,
@@ -180,7 +183,7 @@
* [ContentObserver] registration happens on a worker thread. Caller may wrap the API in an
* async block if they wish to synchronize execution.
*/
- suspend fun registerContentObserverForUser(
+ public suspend fun registerContentObserverForUser(
uri: Uri,
settingsObserver: ContentObserver,
userHandle: Int,
@@ -195,11 +198,11 @@
*
* API corresponding to [registerContentObserverForUser] for Java usage.
*/
- fun registerContentObserverForUserAsync(
+ public fun registerContentObserverForUserAsync(
uri: Uri,
settingsObserver: ContentObserver,
userHandle: Int,
- ) =
+ ): Job =
settingsScope.launch("registerContentObserverForUserAsync-B") {
try {
registerContentObserverForUserSync(uri, settingsObserver, userHandle)
@@ -215,12 +218,12 @@
* complete, the callback block is called on the <b>background thread</b> to allow for update of
* value.
*/
- fun registerContentObserverForUserAsync(
+ public fun registerContentObserverForUserAsync(
uri: Uri,
settingsObserver: ContentObserver,
userHandle: Int,
@WorkerThread registered: Runnable,
- ) =
+ ): Job =
settingsScope.launch("registerContentObserverForUserAsync-C") {
try {
registerContentObserverForUserSync(uri, settingsObserver, userHandle)
@@ -236,7 +239,7 @@
* Implicitly calls [getUriFor] on the passed in name.
*/
@WorkerThread
- fun registerContentObserverForUserSync(
+ public fun registerContentObserverForUserSync(
name: String,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
@@ -257,7 +260,7 @@
* [ContentObserver] registration happens on a worker thread. Caller may wrap the API in an
* async block if they wish to synchronize execution.
*/
- suspend fun registerContentObserverForUser(
+ public suspend fun registerContentObserverForUser(
name: String,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
@@ -278,7 +281,7 @@
*
* API corresponding to [registerContentObserverForUser] for Java usage.
*/
- fun registerContentObserverForUserAsync(
+ public fun registerContentObserverForUserAsync(
name: String,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
@@ -300,7 +303,7 @@
/** Convenience wrapper around [ContentResolver.registerContentObserver] */
@WorkerThread
- fun registerContentObserverForUserSync(
+ public fun registerContentObserverForUserSync(
uri: Uri,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
@@ -314,7 +317,6 @@
settingsObserver,
getRealUserHandle(userHandle),
)
- Unit
}
}
@@ -325,7 +327,7 @@
* [ContentObserver] registration happens on a worker thread. Caller may wrap the API in an
* async block if they wish to synchronize execution.
*/
- suspend fun registerContentObserverForUser(
+ public suspend fun registerContentObserverForUser(
uri: Uri,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
@@ -346,12 +348,12 @@
*
* API corresponding to [registerContentObserverForUser] for Java usage.
*/
- fun registerContentObserverForUserAsync(
+ public fun registerContentObserverForUserAsync(
uri: Uri,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
userHandle: Int,
- ) =
+ ): Job =
settingsScope.launch("registerContentObserverForUserAsync-E") {
try {
registerContentObserverForUserSync(
@@ -376,7 +378,7 @@
}
/** See [getString]. */
- fun getStringForUser(name: String, userHandle: Int): String?
+ public fun getStringForUser(name: String, userHandle: Int): String?
/**
* Store a name/value pair into the database. Values written by this method will be overridden
@@ -386,17 +388,17 @@
* @param value to associate with the name
* @return true if the value was set, false on database errors
*/
- fun putString(name: String, value: String?, overrideableByRestore: Boolean): Boolean
+ public fun putString(name: String, value: String?, overrideableByRestore: Boolean): Boolean
override fun putString(name: String, value: String?): Boolean {
return putStringForUser(name, value, userId)
}
/** Similar implementation to [putString] for the specified [userHandle]. */
- fun putStringForUser(name: String, value: String?, userHandle: Int): Boolean
+ public fun putStringForUser(name: String, value: String?, userHandle: Int): Boolean
/** Similar implementation to [putString] for the specified [userHandle]. */
- fun putStringForUser(
+ public fun putStringForUser(
name: String,
value: String?,
tag: String?,
@@ -410,7 +412,7 @@
}
/** Similar implementation to [getInt] for the specified [userHandle]. */
- fun getIntForUser(name: String, default: Int, userHandle: Int): Int {
+ public fun getIntForUser(name: String, default: Int, userHandle: Int): Int {
val v = getStringForUser(name, userHandle)
return try {
v?.toInt() ?: default
@@ -420,11 +422,11 @@
}
@Throws(SettingNotFoundException::class)
- override fun getInt(name: String) = getIntForUser(name, userId)
+ override fun getInt(name: String): Int = getIntForUser(name, userId)
/** Similar implementation to [getInt] for the specified [userHandle]. */
@Throws(SettingNotFoundException::class)
- fun getIntForUser(name: String, userHandle: Int): Int {
+ public fun getIntForUser(name: String, userHandle: Int): Int {
val v = getStringForUser(name, userHandle) ?: throw SettingNotFoundException(name)
return try {
v.toInt()
@@ -433,24 +435,24 @@
}
}
- override fun putInt(name: String, value: Int) = putIntForUser(name, value, userId)
+ override fun putInt(name: String, value: Int): Boolean = putIntForUser(name, value, userId)
/** Similar implementation to [getInt] for the specified [userHandle]. */
- fun putIntForUser(name: String, value: Int, userHandle: Int) =
+ public fun putIntForUser(name: String, value: Int, userHandle: Int): Boolean =
putStringForUser(name, value.toString(), userHandle)
- override fun getBool(name: String, def: Boolean) = getBoolForUser(name, def, userId)
+ override fun getBool(name: String, def: Boolean): Boolean = getBoolForUser(name, def, userId)
/** Similar implementation to [getBool] for the specified [userHandle]. */
- fun getBoolForUser(name: String, def: Boolean, userHandle: Int) =
+ public fun getBoolForUser(name: String, def: Boolean, userHandle: Int): Boolean =
getIntForUser(name, if (def) 1 else 0, userHandle) != 0
@Throws(SettingNotFoundException::class)
- override fun getBool(name: String) = getBoolForUser(name, userId)
+ override fun getBool(name: String): Boolean = getBoolForUser(name, userId)
/** Similar implementation to [getBool] for the specified [userHandle]. */
@Throws(SettingNotFoundException::class)
- fun getBoolForUser(name: String, userHandle: Int): Boolean {
+ public fun getBoolForUser(name: String, userHandle: Int): Boolean {
return getIntForUser(name, userHandle) != 0
}
@@ -459,40 +461,40 @@
}
/** Similar implementation to [putBool] for the specified [userHandle]. */
- fun putBoolForUser(name: String, value: Boolean, userHandle: Int) =
+ public fun putBoolForUser(name: String, value: Boolean, userHandle: Int): Boolean =
putIntForUser(name, if (value) 1 else 0, userHandle)
/** Similar implementation to [getLong] for the specified [userHandle]. */
- fun getLongForUser(name: String, def: Long, userHandle: Int): Long {
+ public fun getLongForUser(name: String, def: Long, userHandle: Int): Long {
val valString = getStringForUser(name, userHandle)
return parseLongOrUseDefault(valString, def)
}
/** Similar implementation to [getLong] for the specified [userHandle]. */
@Throws(SettingNotFoundException::class)
- fun getLongForUser(name: String, userHandle: Int): Long {
+ public fun getLongForUser(name: String, userHandle: Int): Long {
val valString = getStringForUser(name, userHandle)
return parseLongOrThrow(name, valString)
}
/** Similar implementation to [putLong] for the specified [userHandle]. */
- fun putLongForUser(name: String, value: Long, userHandle: Int) =
+ public fun putLongForUser(name: String, value: Long, userHandle: Int): Boolean =
putStringForUser(name, value.toString(), userHandle)
/** Similar implementation to [getFloat] for the specified [userHandle]. */
- fun getFloatForUser(name: String, def: Float, userHandle: Int): Float {
+ public fun getFloatForUser(name: String, def: Float, userHandle: Int): Float {
val v = getStringForUser(name, userHandle)
return parseFloat(v, def)
}
/** Similar implementation to [getFloat] for the specified [userHandle]. */
@Throws(SettingNotFoundException::class)
- fun getFloatForUser(name: String, userHandle: Int): Float {
+ public fun getFloatForUser(name: String, userHandle: Int): Float {
val v = getStringForUser(name, userHandle)
return parseFloatOrThrow(name, v)
}
/** Similar implementation to [putFloat] for the specified [userHandle]. */
- fun putFloatForUser(name: String, value: Float, userHandle: Int) =
+ public fun putFloatForUser(name: String, value: Float, userHandle: Int): Boolean =
putStringForUser(name, value.toString(), userHandle)
}
diff --git a/packages/SystemUI/res/layout/volume_dialog_top_section.xml b/packages/SystemUI/res/layout/volume_dialog_top_section.xml
index 4fc20e2..29f5248 100644
--- a/packages/SystemUI/res/layout/volume_dialog_top_section.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_top_section.xml
@@ -22,7 +22,6 @@
android:clipChildren="false"
android:clipToPadding="false"
android:gravity="center"
- android:layoutDirection="ltr"
android:paddingEnd="@dimen/volume_dialog_ringer_drawer_diff_end_margin"
app:layoutDescription="@xml/volume_dialog_ringer_drawer_motion_scene">
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6ff1240..43ea2c3 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1024,6 +1024,12 @@
<string name="hearing_devices_ambient_control_left">Left</string>
<!-- QuickSettings: The text to show the control is for right side device. [CHAR LIMIT=30] -->
<string name="hearing_devices_ambient_control_right">Right</string>
+ <!-- QuickSettings: Content description for unified ambient control slider. [CHAR LIMIT=NONE] -->
+ <string name="hearing_devices_ambient_control_description">Surroundings</string>
+ <!-- QuickSettings: Content description for left ambient control slider. [CHAR LIMIT=NONE] -->
+ <string name="hearing_devices_ambient_control_left_description">Left surroundings</string>
+ <!-- QuickSettings: Content description for left ambient control slider. [CHAR LIMIT=NONE] -->
+ <string name="hearing_devices_ambient_control_right_description">Right surroundings</string>
<!-- QuickSettings: Content description for a button, that expands ambient volume sliders [CHAR_LIMIT=NONE] -->
<string name="hearing_devices_ambient_expand_controls">Expand to left and right separated controls</string>
<!-- QuickSettings: Content description for a button, that collapses ambient volume sliders [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 5b96e50..4431dda 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -871,6 +871,7 @@
</style>
<style name="MediaPlayer.SessionAction.Primary" parent="MediaPlayer.SessionAction">
+ <item name="android:tint">@android:color/system_on_primary_dark</item>
<item name="android:background">@drawable/qs_media_round_button_background</item>
<item name="android:backgroundTint">@color/media_player_solid_button_bg</item>
</style>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 63101d4..bd09e39 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -199,7 +199,7 @@
* to be updated.
*/
@SysUISingleton
-public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpable, CoreStartable {
+public class KeyguardUpdateMonitor implements TrustManager.TrustListener, CoreStartable {
private static final String TAG = "KeyguardUpdateMonitor";
private static final int BIOMETRIC_LOCKOUT_RESET_DELAY_MS = 600;
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index e725353..19da5de 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -112,7 +112,7 @@
*/
@SysUISingleton
public class ScreenDecorations implements
- CoreStartable, ConfigurationController.ConfigurationListener, Dumpable {
+ CoreStartable, ConfigurationController.ConfigurationListener {
private static final boolean DEBUG_LOGGING = false;
private static final String TAG = "ScreenDecorations";
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 85f1880..c78f75a 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -54,6 +54,7 @@
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization;
import com.android.wm.shell.animation.FlingAnimationUtils;
import com.android.wm.shell.shared.animation.PhysicsAnimator;
@@ -890,12 +891,16 @@
if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_DRAG_TO_CONTENTS)) {
if (v instanceof ExpandableNotificationRow) {
ExpandableNotificationRow enr = (ExpandableNotificationRow) v;
- boolean canBubble = enr.getEntry().canBubble();
- Notification notif = enr.getEntry().getSbn().getNotification();
- PendingIntent dragIntent = notif.contentIntent != null ? notif.contentIntent
- : notif.fullScreenIntent;
- if (dragIntent != null && dragIntent.isActivity() && !canBubble) {
- return true;
+ if (NotificationBundleUi.isEnabled()) {
+ return enr.getEntryAdapter().canDragAndDrop();
+ } else {
+ boolean canBubble = enr.getEntry().canBubble();
+ Notification notif = enr.getEntry().getSbn().getNotification();
+ PendingIntent dragIntent = notif.contentIntent != null ? notif.contentIntent
+ : notif.fullScreenIntent;
+ if (dragIntent != null && dragIntent.isActivity() && !canBubble) {
+ return true;
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayout.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayout.java
index 5247acc..33c9eb1 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayout.java
@@ -327,8 +327,19 @@
slider.addOnChangeListener(mSliderOnChangeListener);
if (side == SIDE_LEFT) {
slider.setTitle(mContext.getString(R.string.hearing_devices_ambient_control_left));
+ slider.setContentDescription(
+ mContext.getString(R.string.hearing_devices_ambient_control_left));
+ slider.setSliderContentDescription(
+ mContext.getString(R.string.hearing_devices_ambient_control_left_description));
} else if (side == SIDE_RIGHT) {
slider.setTitle(mContext.getString(R.string.hearing_devices_ambient_control_right));
+ slider.setContentDescription(
+ mContext.getString(R.string.hearing_devices_ambient_control_right));
+ slider.setSliderContentDescription(
+ mContext.getString(R.string.hearing_devices_ambient_control_right_description));
+ } else {
+ slider.setSliderContentDescription(
+ mContext.getString(R.string.hearing_devices_ambient_control_description));
}
mSideToSliderMap.put(side, slider);
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeSlider.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeSlider.java
index 1a068c4..5c0ad3d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeSlider.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeSlider.java
@@ -92,6 +92,11 @@
mSlider = requireViewById(R.id.ambient_volume_slider);
mSlider.addOnSliderTouchListener(mSliderTouchListener);
mSlider.addOnChangeListener(mSliderChangeListener);
+
+ setFocusable(false);
+ setClickable(false);
+ mSlider.setFocusable(false);
+ mSlider.setClickable(false);
}
/**
@@ -178,6 +183,13 @@
return (int) Math.ceil((value - min) / levelGap);
}
+ /** Sets the content description to the ambient volume slider. */
+ public void setSliderContentDescription(CharSequence contentDescription) {
+ if (mSlider != null) {
+ mSlider.setContentDescription(contentDescription);
+ }
+ }
+
/** Interface definition for a callback invoked when a slider's value is changed. */
public interface OnChangeListener {
/** Called when the finger is take off from the slider. */
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index 4dcf268..0902d19 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -34,6 +34,7 @@
import android.util.RotationUtils
import android.view.HapticFeedbackConstants
import android.view.MotionEvent
+import android.view.accessibility.AccessibilityManager
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.keyguard.AuthInteractionProperties
import com.android.launcher3.icons.IconProvider
@@ -85,7 +86,17 @@
private val udfpsUtils: UdfpsUtils,
private val iconProvider: IconProvider,
private val activityTaskManager: ActivityTaskManager,
+ private val accessibilityManager: AccessibilityManager,
) {
+ // When a11y enabled, increase message delay to ensure messages get read
+ private val messageDelay =
+ accessibilityManager
+ .getRecommendedTimeoutMillis(
+ BiometricPrompt.HIDE_DIALOG_DELAY,
+ AccessibilityManager.FLAG_CONTENT_CONTROLS or AccessibilityManager.FLAG_CONTENT_TEXT,
+ )
+ .toLong()
+
/** The set of modalities available for this prompt */
val modalities: Flow<BiometricModalities> =
promptSelectorInteractor.prompt
@@ -692,7 +703,7 @@
messageJob?.cancel()
messageJob = launch {
- delay(BiometricPrompt.HIDE_DIALOG_DELAY.toLong())
+ delay(messageDelay)
if (authenticateAfterError) {
showAuthenticating(messageAfterError)
} else {
@@ -754,7 +765,7 @@
messageJob?.cancel()
messageJob = launch {
- delay(BiometricPrompt.HIDE_DIALOG_DELAY.toLong())
+ delay(messageDelay)
showAuthenticating(messageAfterHelp)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt
deleted file mode 100644
index b531d15..0000000
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt
+++ /dev/null
@@ -1,124 +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.communal.ui.viewmodel
-
-import android.annotation.SuppressLint
-import android.app.DreamManager
-import android.content.Intent
-import android.provider.Settings
-import androidx.compose.runtime.getValue
-import com.android.internal.logging.UiEventLogger
-import com.android.systemui.communal.domain.interactor.CommunalPrefsInteractor
-import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
-import com.android.systemui.communal.shared.log.CommunalUiEvent
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.lifecycle.ExclusiveActivatable
-import com.android.systemui.lifecycle.Hydrator
-import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.statusbar.policy.BatteryController
-import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
-import com.android.systemui.util.kotlin.BooleanFlowOperators.not
-import com.android.systemui.util.kotlin.isDevicePluggedIn
-import com.android.systemui.util.kotlin.sample
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
-import kotlin.coroutines.CoroutineContext
-import kotlinx.coroutines.awaitCancellation
-import kotlinx.coroutines.channels.Channel
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.receiveAsFlow
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-
-class CommunalToDreamButtonViewModel
-@AssistedInject
-constructor(
- @Background private val backgroundContext: CoroutineContext,
- batteryController: BatteryController,
- private val prefsInteractor: CommunalPrefsInteractor,
- private val settingsInteractor: CommunalSettingsInteractor,
- private val activityStarter: ActivityStarter,
- private val dreamManager: DreamManager,
- private val uiEventLogger: UiEventLogger,
-) : ExclusiveActivatable() {
-
- private val hydrator = Hydrator("CommunalToDreamButtonViewModel.hydrator")
- private val _requests = Channel<Unit>(Channel.BUFFERED)
-
- /** Whether we should show a button on hub to switch to dream. */
- val shouldShowDreamButtonOnHub: Boolean by
- hydrator.hydratedStateOf(
- traceName = "shouldShowDreamButtonOnHub",
- initialValue = false,
- source = batteryController.isDevicePluggedIn().distinctUntilChanged(),
- )
-
- /** Return whether to show the dream button tooltip. */
- val shouldShowTooltip: Boolean by
- hydrator.hydratedStateOf(
- traceName = "shouldShowTooltip",
- initialValue = false,
- source =
- allOf(
- not(prefsInteractor.isDreamButtonTooltipDismissed),
- prefsInteractor.isHubOnboardingDismissed,
- ),
- )
-
- /** Set the dream button tooltip to be dismissed. */
- fun setDreamButtonTooltipDismissed() {
- prefsInteractor.setDreamButtonTooltipDismissed()
- }
-
- /** Handle a tap on the "show dream" button. */
- fun onShowDreamButtonTap() {
- uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_SHOW_DREAM_BUTTON_TAP)
- _requests.trySend(Unit)
- }
-
- @SuppressLint("MissingPermission")
- override suspend fun onActivated(): Nothing = coroutineScope {
- launch {
- _requests
- .receiveAsFlow()
- .sample(settingsInteractor.isScreensaverEnabled)
- .collectLatest { enabled ->
- withContext(backgroundContext) {
- if (enabled) {
- dreamManager.startDream()
- } else {
- activityStarter.postStartActivityDismissingKeyguard(
- Intent(Settings.ACTION_DREAM_SETTINGS),
- 0,
- )
- }
- }
- }
- }
-
- launch { hydrator.activate() }
-
- awaitCancellation()
- }
-
- @AssistedFactory
- interface Factory {
- fun create(): CommunalToDreamButtonViewModel
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
index d464200..721d116 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
@@ -26,14 +26,16 @@
import android.os.Handler
import android.util.Log
import android.view.Display
+import android.view.IWindowManager
import com.android.app.tracing.FlowTracing.traceEach
import com.android.app.tracing.traceSection
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.display.data.DisplayEvent
+import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.util.Compile
import com.android.systemui.util.kotlin.pairwiseBy
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
@@ -43,6 +45,7 @@
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asFlow
+import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
@@ -50,12 +53,13 @@
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.scan
import kotlinx.coroutines.flow.stateIn
-/** Provides a [Flow] of [Display] as returned by [DisplayManager]. */
+/** Repository for providing access to display related information and events. */
interface DisplayRepository {
/** Display change event indicating a change to the given displayId has occurred. */
val displayChangeEvent: Flow<Int>
@@ -66,6 +70,9 @@
/** Display removal event indicating a display has been removed. */
val displayRemovalEvent: Flow<Int>
+ /** A [StateFlow] that maintains a set of display IDs that should have system decorations. */
+ val displayIdsWithSystemDecorations: StateFlow<Set<Int>>
+
/**
* Provides the current set of displays.
*
@@ -124,6 +131,8 @@
@Inject
constructor(
private val displayManager: DisplayManager,
+ private val commandQueue: CommandQueue,
+ private val windowManager: IWindowManager,
@Background backgroundHandler: Handler,
@Background bgApplicationScope: CoroutineScope,
@Background backgroundCoroutineDispatcher: CoroutineDispatcher,
@@ -426,6 +435,56 @@
.map { it.resultSet }
}
+ private val decorationEvents: Flow<Event> = callbackFlow {
+ val callback =
+ object : CommandQueue.Callbacks {
+ override fun onDisplayAddSystemDecorations(displayId: Int) {
+ trySend(Event.Add(displayId))
+ }
+
+ override fun onDisplayRemoveSystemDecorations(displayId: Int) {
+ trySend(Event.Remove(displayId))
+ }
+ }
+ commandQueue.addCallback(callback)
+ awaitClose { commandQueue.removeCallback(callback) }
+ }
+
+ private val initialDisplayIdsWithDecorations: Set<Int> =
+ displayIds.value.filter { windowManager.shouldShowSystemDecors(it) }.toSet()
+
+ /**
+ * A [StateFlow] that maintains a set of display IDs that should have system decorations.
+ *
+ * Updates to the set are triggered by:
+ * - Adding displays via [CommandQueue.Callbacks.onDisplayAddSystemDecorations].
+ * - Removing displays via [CommandQueue.Callbacks.onDisplayRemoveSystemDecorations].
+ * - Removing displays via [displayRemovalEvent] emissions.
+ *
+ * The set is initialized with displays that qualify for system decorations based on
+ * [WindowManager.shouldShowSystemDecors].
+ */
+ override val displayIdsWithSystemDecorations: StateFlow<Set<Int>> =
+ merge(decorationEvents, displayRemovalEvent.map { Event.Remove(it) })
+ .scan(initialDisplayIdsWithDecorations) { displayIds: Set<Int>, event: Event ->
+ when (event) {
+ is Event.Add -> displayIds + event.displayId
+ is Event.Remove -> displayIds - event.displayId
+ }
+ }
+ .distinctUntilChanged()
+ .stateIn(
+ scope = bgApplicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = initialDisplayIdsWithDecorations,
+ )
+
+ private sealed class Event(val displayId: Int) {
+ class Add(displayId: Int) : Event(displayId)
+
+ class Remove(displayId: Int) : Event(displayId)
+ }
+
private companion object {
const val TAG = "DisplayRepository"
val DEBUG = Log.isLoggable(TAG, Log.DEBUG) || Compile.IS_DEBUG
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index efa9c21..cc0efbc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -241,7 +241,7 @@
* directly to the keyguard UI is posted to a {@link android.os.Handler} to ensure it is taken on the UI
* thread of the keyguard.
*/
-public class KeyguardViewMediator implements CoreStartable, Dumpable,
+public class KeyguardViewMediator implements CoreStartable,
StatusBarStateController.StateListener {
private static final boolean ENABLE_NEW_KEYGUARD_SHELL_TRANSITIONS =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
index 7a4be1d..fc5914b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
@@ -22,6 +22,7 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.systemui.Flags
import com.android.systemui.customization.R as customR
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition
@@ -81,7 +82,7 @@
logger.logConstraintSet(cs, clockViewModel)
cs.applyTo(constraintLayout)
- if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+ if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
manuallySetDateWeatherConstraintsOnConstraintLayout(
cs,
constraintLayout,
@@ -110,7 +111,7 @@
}
logger.logConstraintSet(cs, clockViewModel)
cs.applyTo(constraintLayout)
- if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+ if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
manuallySetDateWeatherConstraintsOnConstraintLayout(
cs,
constraintLayout,
@@ -168,7 +169,7 @@
str1 = "${cs.getConstraint(smartspaceDateId).propertySet.alpha}"
}
- if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+ if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
this.i({ "applyCsToSmartspaceWeather: vis=${getVisText(int1)}; alpha=$str1" }) {
val smartspaceDateId = sharedR.id.weather_smartspace_view
int1 = cs.getVisibility(smartspaceDateId)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index b69df68..45801ba 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -89,7 +89,6 @@
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
@@ -189,7 +188,7 @@
viewModel.translationY.collect { y ->
childViews[burnInLayerId]?.translationY = y
childViews[largeClockId]?.translationY = y
- if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+ if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
childViews[largeClockDateId]?.translationY = y
childViews[largeClockWeatherId]?.translationY = y
}
@@ -381,17 +380,9 @@
if (wallpaperFocalAreaViewModel.hasFocalArea.value) {
launch {
wallpaperFocalAreaViewModel.wallpaperFocalAreaBounds.collect {
- wallpaperFocalAreaBounds ->
- wallpaperFocalAreaViewModel.setFocalAreaBounds(
- wallpaperFocalAreaBounds
- )
+ wallpaperFocalAreaViewModel.setFocalAreaBounds(it)
}
}
- launch {
- wallpaperFocalAreaViewModel.wallpaperFocalAreaBounds
- .filterNotNull()
- .collect { wallpaperFocalAreaViewModel.setFocalAreaBounds(it) }
- }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
index 76ece7d..def1ac8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
@@ -80,7 +80,7 @@
}
}
- if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+ if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
launch("$TAG#smartspaceViewModel.burnInLayerVisibility") {
keyguardRootViewModel.burnInLayerVisibility.collect { visibility ->
if (clockViewModel.isLargeClockVisible.value) {
@@ -147,7 +147,7 @@
val dateView =
constraintLayout.requireViewById<View>(sharedR.id.date_smartspace_view)
addView(dateView)
- if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+ if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
val weatherView =
constraintLayout.requireViewById<View>(sharedR.id.weather_smartspace_view)
addView(weatherView)
@@ -169,7 +169,7 @@
val dateView =
constraintLayout.requireViewById<View>(sharedR.id.date_smartspace_view)
removeView(dateView)
- if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+ if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
val weatherView =
constraintLayout.requireViewById<View>(sharedR.id.weather_smartspace_view)
removeView(weatherView)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
index 3d5dbb6d..8a33c64 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
@@ -121,19 +121,19 @@
setAlpha(getNonTargetClockFace(clock).views, 0F)
if (!keyguardClockViewModel.isLargeClockVisible.value) {
- if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+ if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
connect(
sharedR.id.bc_smartspace_view,
TOP,
customR.id.lockscreen_clock_view,
- BOTTOM
+ BOTTOM,
)
} else {
connect(
sharedR.id.bc_smartspace_view,
TOP,
sharedR.id.date_smartspace_view,
- BOTTOM
+ BOTTOM,
)
}
} else {
@@ -161,7 +161,7 @@
)
if (
rootViewModel.isNotifIconContainerVisible.value.value &&
- keyguardClockViewModel.hasAodIcons.value
+ keyguardClockViewModel.hasAodIcons.value
) {
createBarrier(
R.id.weather_clock_date_and_icons_barrier_bottom,
@@ -197,13 +197,13 @@
TOP,
)
val largeClockTopMargin =
- if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+ if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
keyguardClockViewModel.getLargeClockTopMargin() +
- getDimen(ENHANCED_SMARTSPACE_HEIGHT)
+ getDimen(ENHANCED_SMARTSPACE_HEIGHT)
} else {
keyguardClockViewModel.getLargeClockTopMargin() +
- getDimen(DATE_WEATHER_VIEW_HEIGHT) +
- getDimen(ENHANCED_SMARTSPACE_HEIGHT)
+ getDimen(DATE_WEATHER_VIEW_HEIGHT) +
+ getDimen(ENHANCED_SMARTSPACE_HEIGHT)
}
connect(
customR.id.lockscreen_clock_view_large,
@@ -229,9 +229,9 @@
PARENT_ID,
START,
context.resources.getDimensionPixelSize(customR.dimen.clock_padding_start) +
- context.resources.getDimensionPixelSize(
- customR.dimen.status_view_margin_horizontal
- ),
+ context.resources.getDimensionPixelSize(
+ customR.dimen.status_view_margin_horizontal
+ ),
)
val smallClockTopMargin = keyguardClockViewModel.getSmallClockTopMargin()
create(R.id.small_clock_guideline_top, ConstraintSet.HORIZONTAL_GUIDELINE)
@@ -243,18 +243,18 @@
val smallClockBottom =
keyguardClockViewModel.getSmallClockTopMargin() +
- context.resources.getDimensionPixelSize(customR.dimen.small_clock_height)
+ context.resources.getDimensionPixelSize(customR.dimen.small_clock_height)
val marginBetweenSmartspaceAndNotification =
context.resources.getDimensionPixelSize(
R.dimen.keyguard_status_view_bottom_margin
) +
- if (context.resources.getBoolean(R.bool.config_use_large_screen_shade_header)) {
- largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight()
- } else {
- 0
- }
+ if (context.resources.getBoolean(R.bool.config_use_large_screen_shade_header)) {
+ largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight()
+ } else {
+ 0
+ }
- if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+ if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
clockInteractor.setNotificationStackDefaultTop(
(smallClockBottom + marginBetweenSmartspaceAndNotification).toFloat()
)
@@ -263,8 +263,8 @@
getDimen(context, DATE_WEATHER_VIEW_HEIGHT).toFloat()
clockInteractor.setNotificationStackDefaultTop(
smallClockBottom +
- dateWeatherSmartspaceHeight +
- marginBetweenSmartspaceAndNotification
+ dateWeatherSmartspaceHeight +
+ marginBetweenSmartspaceAndNotification
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
index cbd80b4..37cc852f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
@@ -80,7 +80,7 @@
weatherView = smartspaceController.buildAndConnectWeatherView(constraintLayout, false)
dateView =
smartspaceController.buildAndConnectDateView(constraintLayout, false) as? ViewGroup
- if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+ if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
weatherViewLargeClock =
smartspaceController.buildAndConnectWeatherView(constraintLayout, true)
dateViewLargeClock =
@@ -88,7 +88,7 @@
}
pastVisibility = smartspaceView?.visibility ?: View.GONE
constraintLayout.addView(smartspaceView)
- if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+ if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
dateView?.visibility = View.GONE
weatherView?.visibility = View.GONE
dateViewLargeClock?.visibility = View.GONE
@@ -139,7 +139,7 @@
constraintSet.apply {
constrainHeight(sharedR.id.date_smartspace_view, ConstraintSet.WRAP_CONTENT)
constrainWidth(sharedR.id.date_smartspace_view, ConstraintSet.WRAP_CONTENT)
- if (!com.android.systemui.shared.Flags.clockReactiveVariants()) {
+ if (!com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
connect(
sharedR.id.date_smartspace_view,
ConstraintSet.START,
@@ -167,7 +167,7 @@
smartspaceHorizontalPadding,
)
if (keyguardClockViewModel.hasCustomWeatherDataDisplay.value) {
- if (!com.android.systemui.shared.Flags.clockReactiveVariants()) {
+ if (!com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
clear(sharedR.id.date_smartspace_view, ConstraintSet.TOP)
connect(
sharedR.id.date_smartspace_view,
@@ -178,7 +178,7 @@
}
} else {
clear(sharedR.id.date_smartspace_view, ConstraintSet.BOTTOM)
- if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+ if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
connect(
sharedR.id.bc_smartspace_view,
ConstraintSet.TOP,
@@ -201,7 +201,7 @@
}
}
- if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+ if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
if (keyguardClockViewModel.isLargeClockVisible.value) {
setVisibility(sharedR.id.weather_smartspace_view, GONE)
setVisibility(sharedR.id.date_smartspace_view, GONE)
@@ -335,7 +335,7 @@
}
}
- if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+ if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
createBarrier(
R.id.smart_space_barrier_bottom,
Barrier.BOTTOM,
@@ -370,7 +370,7 @@
if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return
val list =
- if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+ if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
listOf(
smartspaceView,
dateView,
@@ -401,7 +401,7 @@
val weatherId: Int
val dateId: Int
if (
- com.android.systemui.shared.Flags.clockReactiveVariants() &&
+ com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout() &&
keyguardClockViewModel.isLargeClockVisible.value
) {
weatherId = sharedR.id.weather_smartspace_view_large
@@ -420,7 +420,7 @@
setVisibility(dateId, if (showDateView) VISIBLE else GONE)
setAlpha(dateId, if (showDateView) 1f else 0f)
- if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+ if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
if (keyguardClockViewModel.isLargeClockVisible.value) {
setVisibility(sharedR.id.weather_smartspace_view, GONE)
setVisibility(sharedR.id.date_smartspace_view, GONE)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
index 67158e2..434d7ea 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
@@ -297,14 +297,14 @@
logger.e("No large clock set, falling back")
addTarget(customR.id.lockscreen_clock_view_large)
}
- if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+ if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
addTarget(sharedR.id.date_smartspace_view_large)
addTarget(sharedR.id.weather_smartspace_view_large)
}
} else {
logger.i("Adding small clock")
addTarget(customR.id.lockscreen_clock_view)
- if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+ if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
addTarget(sharedR.id.date_smartspace_view)
addTarget(sharedR.id.weather_smartspace_view)
}
@@ -386,7 +386,7 @@
duration =
if (isLargeClock) STATUS_AREA_MOVE_UP_MILLIS else STATUS_AREA_MOVE_DOWN_MILLIS
interpolator = Interpolators.EMPHASIZED
- if (!com.android.systemui.shared.Flags.clockReactiveVariants()) {
+ if (!com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
addTarget(sharedR.id.date_smartspace_view)
}
addTarget(sharedR.id.bc_smartspace_view)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/DefaultClockSteppingTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/DefaultClockSteppingTransition.kt
index 827c61e..0874b6d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/DefaultClockSteppingTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/DefaultClockSteppingTransition.kt
@@ -25,14 +25,12 @@
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.shared.R as sharedR
-class DefaultClockSteppingTransition(
- private val clock: ClockController,
-) : Transition() {
+class DefaultClockSteppingTransition(private val clock: ClockController) : Transition() {
init {
interpolator = Interpolators.LINEAR
duration = KEYGUARD_STATUS_VIEW_CUSTOM_CLOCK_MOVE_DURATION_MS
addTarget(clock.largeClock.view)
- if (com.android.systemui.shared.Flags.clockReactiveVariants()) {
+ if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
addTarget(sharedR.id.date_smartspace_view_large)
addTarget(sharedR.id.weather_smartspace_view_large)
}
@@ -56,7 +54,7 @@
override fun createAnimator(
sceneRoot: ViewGroup,
startValues: TransitionValues?,
- endValues: TransitionValues?
+ endValues: TransitionValues?,
): Animator? {
if (startValues == null || endValues == null) {
return null
@@ -72,7 +70,7 @@
clock.largeClock.animations.onPositionUpdated(
fromLeft,
direction,
- animation.animatedFraction
+ animation.animatedFraction,
)
}
return anim
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaProcessingHelper.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaProcessingHelper.kt
index beb4d41..df0e1ad 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaProcessingHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaProcessingHelper.kt
@@ -22,6 +22,7 @@
import android.graphics.drawable.Icon
import android.media.session.MediaController
import android.media.session.PlaybackState
+import android.os.BadParcelableException
import android.util.Log
import com.android.systemui.Flags.mediaControlsPostsOptimization
import com.android.systemui.biometrics.Utils.toBitmap
@@ -109,7 +110,12 @@
}
if (firstAction.extras != null) {
firstAction.extras.keySet().forEach { key ->
- if (firstAction.extras[key] != secondAction.extras[key]) {
+ try {
+ if (firstAction.extras[key] != secondAction.extras[key]) {
+ return false
+ }
+ } catch (e: BadParcelableException) {
+ Log.e(TAG, "Cannot unparcel extras", e)
return false
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
index 34f7c4d..cedf661 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
@@ -127,10 +127,9 @@
}
holder.seekBar.setMax(data.duration)
- val totalTimeString =
- DateUtils.formatElapsedTime(data.duration / DateUtils.SECOND_IN_MILLIS)
+ val totalTimeDescription = data.durationDescription
if (data.scrubbing) {
- holder.scrubbingTotalTimeView.text = totalTimeString
+ holder.scrubbingTotalTimeView.text = formatTimeLabel(data.duration)
}
data.elapsedTime?.let {
@@ -148,20 +147,25 @@
}
}
- val elapsedTimeString = DateUtils.formatElapsedTime(it / DateUtils.SECOND_IN_MILLIS)
+ val elapsedTimeDescription = data.elapsedTimeDescription
if (data.scrubbing) {
- holder.scrubbingElapsedTimeView.text = elapsedTimeString
+ holder.scrubbingElapsedTimeView.text = formatTimeLabel(it)
}
holder.seekBar.contentDescription =
holder.seekBar.context.getString(
R.string.controls_media_seekbar_description,
- elapsedTimeString,
- totalTimeString
+ elapsedTimeDescription,
+ totalTimeDescription,
)
}
}
+ /** Returns a time string suitable for display, e.g. "12:34" */
+ private fun formatTimeLabel(milliseconds: Int): CharSequence {
+ return DateUtils.formatElapsedTime(milliseconds / DateUtils.SECOND_IN_MILLIS)
+ }
+
@VisibleForTesting
open fun buildResetAnimator(targetTime: Int): Animator {
val animator =
@@ -169,7 +173,7 @@
holder.seekBar,
"progress",
holder.seekBar.progress,
- targetTime + RESET_ANIMATION_DURATION_MS
+ targetTime + RESET_ANIMATION_DURATION_MS,
)
animator.setAutoCancel(true)
animator.duration = RESET_ANIMATION_DURATION_MS.toLong()
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt
index 9b443f5..5d62c02 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt
@@ -90,15 +90,16 @@
fun logCarouselVisible() = buffer.log(TAG, LogLevel.DEBUG, {}, { "showing carousel" })
- fun logMediaHostVisibility(location: Int, visible: Boolean) {
+ fun logMediaHostVisibility(location: Int, visible: Boolean, oldState: Boolean) {
buffer.log(
TAG,
LogLevel.DEBUG,
{
int1 = location
bool1 = visible
+ bool2 = oldState
},
- { "media host visibility changed location=$location, visible:$visible" },
+ { "media host visibility changed location=$location, visible:$visible, was:$oldState" },
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
index c689408..69006c6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
@@ -1129,10 +1129,11 @@
traceSection("MediaHierarchyManager#updateHostAttachment") {
if (SceneContainerFlag.isEnabled) {
// No need to manage transition states - just update the desired location directly
- logger.logMediaHostAttachment(desiredLocation)
+ val host = getHost(desiredLocation)
+ logger.logMediaHostAttachment(desiredLocation, host?.visible)
mediaCarouselController.onDesiredLocationChanged(
desiredLocation = desiredLocation,
- desiredHostState = getHost(desiredLocation),
+ desiredHostState = host,
animate = false,
)
return
@@ -1169,7 +1170,8 @@
// that and directly set the mediaFrame's bounds within the premeasured host.
targetHost.addView(mediaFrame)
}
- logger.logMediaHostAttachment(currentAttachmentLocation)
+ val host = getHost(currentAttachmentLocation)
+ logger.logMediaHostAttachment(currentAttachmentLocation, host?.visible)
if (isCrossFadeAnimatorRunning) {
// When cross-fading with an animation, we only notify the media carousel of the
// location change, once the view is reattached to the new place and not
@@ -1313,6 +1315,7 @@
isHomeScreenShadeVisibleToUser() ||
isGlanceableHubVisibleToUser()
val mediaVisible = qsExpanded || hasActiveMediaOrRecommendation
+ logger.logUserVisibilityChange(shadeVisible, mediaVisible)
mediaCarouselController.mediaCarouselScrollHandler.visibleToUser =
shadeVisible && mediaVisible
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewLogger.kt
index 1514db3..089d16b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewLogger.kt
@@ -36,7 +36,7 @@
int1 = width
int2 = height
},
- { "size ($str1): $int1 x $int2" }
+ { "size ($str1): $int1 x $int2" },
)
}
@@ -49,11 +49,31 @@
int1 = startLocation
int2 = endLocation
},
- { "location ($str1): $int1 -> $int2" }
+ { "location ($str1): $int1 -> $int2" },
)
}
- fun logMediaHostAttachment(host: Int) {
- buffer.log(TAG, LogLevel.DEBUG, { int1 = host }, { "Host (updateHostAttachment): $int1" })
+ fun logMediaHostAttachment(host: Int, visible: Boolean?) {
+ buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ int1 = host
+ str1 = visible.toString()
+ },
+ { "Host (updateHostAttachment): $int1 visible $str1" },
+ )
+ }
+
+ fun logUserVisibilityChange(shadeVisible: Boolean, mediaVisible: Boolean) {
+ buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ bool1 = shadeVisible
+ bool2 = mediaVisible
+ },
+ { "User visibility shade: $shadeVisible media: $mediaVisible" },
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
index 11251cd..a518349 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
@@ -208,6 +208,7 @@
* the visibility has changed
*/
fun updateViewVisibility() {
+ val oldState = state.visible
state.visible =
if (mediaCarouselController.isLockedAndHidden()) {
false
@@ -217,9 +218,9 @@
mediaDataManager.hasAnyMediaOrRecommendation()
}
val newVisibility = if (visible) View.VISIBLE else View.GONE
- if (newVisibility != hostView.visibility) {
+ if (oldState != state.visible || newVisibility != hostView.visibility) {
hostView.visibility = newVisibility
- debugLogger.logMediaHostVisibility(location, visible)
+ debugLogger.logMediaHostVisibility(location, visible, oldState)
visibleChangedListeners.forEach { it.invoke(visible) }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
index da7053b..a1f0cc3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
@@ -16,11 +16,15 @@
package com.android.systemui.media.controls.ui.viewmodel
+import android.icu.text.MeasureFormat
+import android.icu.util.Measure
+import android.icu.util.MeasureUnit
import android.media.MediaMetadata
import android.media.session.MediaController
import android.media.session.PlaybackState
import android.os.SystemClock
import android.os.Trace
+import android.text.format.DateUtils
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View
@@ -38,11 +42,14 @@
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.NotificationMediaManager
import com.android.systemui.util.concurrency.RepeatableExecutor
+import java.util.Locale
import javax.inject.Inject
import kotlin.math.abs
private const val POSITION_UPDATE_INTERVAL_MILLIS = 500L
private const val MIN_FLING_VELOCITY_SCALE_FACTOR = 10
+private const val MIN_IN_SEC = 60
+private const val HOUR_IN_SEC = MIN_IN_SEC * 60
private const val TRACE_POSITION_NAME = "SeekBarPollingPosition"
@@ -97,11 +104,20 @@
)
set(value) {
val enabledChanged = value.enabled != field.enabled
- field = value
if (enabledChanged) {
enabledChangeListener?.onEnabledChanged(value.enabled)
}
- _progress.postValue(value)
+ bgExecutor.execute {
+ val durationDescription = formatTimeContentDescription(value.duration)
+ val elapsedDescription =
+ value.elapsedTime?.let { formatTimeContentDescription(it) } ?: ""
+ field =
+ value.copy(
+ durationDescription = durationDescription,
+ elapsedTimeDescription = elapsedDescription,
+ )
+ _progress.postValue(field)
+ }
}
private val _progress = MutableLiveData<Progress>().apply { postValue(_data) }
@@ -253,7 +269,8 @@
playbackState?.state ?: PlaybackState.STATE_NONE
)
_data = Progress(enabled, seekAvailable, playing, scrubbing, position, duration, listening)
- checkIfPollingNeeded()
+ // No need to update since we just set the progress info
+ checkIfPollingNeeded(requireUpdate = false)
}
/**
@@ -311,8 +328,13 @@
}
}
+ /**
+ * Begin polling if needed given the current seekbar state
+ *
+ * @param requireUpdate If true, update the playback position without beginning polling
+ */
@WorkerThread
- private fun checkIfPollingNeeded() {
+ private fun checkIfPollingNeeded(requireUpdate: Boolean = true) {
val needed = listening && !scrubbing && playbackState?.isInMotion() ?: false
val traceCookie = controller?.sessionToken.hashCode()
if (needed) {
@@ -329,7 +351,7 @@
Trace.endAsyncSection(TRACE_POSITION_NAME, traceCookie)
}
}
- } else {
+ } else if (requireUpdate) {
checkPlaybackPosition()
cancel?.run()
cancel = null
@@ -399,6 +421,43 @@
abs(firstMotionEvent!!.y - lastMotionEvent!!.y)
}
+ /**
+ * Returns a time string suitable for content description, e.g. "12 minutes 34 seconds"
+ *
+ * Follows same logic as Chronometer#formatDuration
+ */
+ private fun formatTimeContentDescription(milliseconds: Int): CharSequence {
+ var seconds = milliseconds / DateUtils.SECOND_IN_MILLIS
+
+ val hours =
+ if (seconds >= HOUR_IN_SEC) {
+ seconds / HOUR_IN_SEC
+ } else {
+ 0
+ }
+ seconds -= hours * HOUR_IN_SEC
+
+ val minutes =
+ if (seconds >= MIN_IN_SEC) {
+ seconds / MIN_IN_SEC
+ } else {
+ 0
+ }
+ seconds -= minutes * MIN_IN_SEC
+
+ val measures = arrayListOf<Measure>()
+ if (hours > 0) {
+ measures.add(Measure(hours, MeasureUnit.HOUR))
+ }
+ if (minutes > 0) {
+ measures.add(Measure(minutes, MeasureUnit.MINUTE))
+ }
+ measures.add(Measure(seconds, MeasureUnit.SECOND))
+
+ return MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE)
+ .formatMeasures(*measures.toTypedArray())
+ }
+
/** Listener interface to be notified when the user starts or stops scrubbing. */
interface ScrubbingChangeListener {
fun onScrubbingChanged(scrubbing: Boolean)
@@ -580,5 +639,7 @@
val duration: Int,
/** whether seekBar is listening to progress updates */
val listening: Boolean,
+ val elapsedTimeDescription: CharSequence = "",
+ val durationDescription: CharSequence = "",
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
index 6ca0471..469bec7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
@@ -24,7 +24,6 @@
import com.android.internal.logging.UiEventLogger
import com.android.internal.statusbar.IUndoMediaTransferCallback
import com.android.systemui.CoreStartable
-import com.android.systemui.Dumpable
import com.android.systemui.common.shared.model.Text
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
@@ -53,7 +52,7 @@
private val dumpManager: DumpManager,
private val logger: MediaTttSenderLogger,
private val uiEventLogger: MediaTttSenderUiEventLogger,
-) : CoreStartable, Dumpable {
+) : CoreStartable {
// Since the media transfer display is similar to a heads-up notification, use the same timeout.
private val defaultTimeout = context.resources.getInteger(R.integer.heads_up_notification_decay)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 934404d..05ef164 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -562,12 +562,15 @@
var userId: Int = lockScreenUserManager.getCurrentUserId()
var entry: NotificationEntry? = null
if (expandView is ExpandableNotificationRow) {
- entry = expandView.entry
expandView.setUserExpanded(/* userExpanded= */ true, /* allowChildExpansion= */ true)
// Indicate that the group expansion is changing at this time -- this way the group
// and children backgrounds / divider animations will look correct.
expandView.isGroupExpansionChanging = true
- userId = entry.sbn.userId
+ if (NotificationBundleUi.isEnabled) {
+ userId = expandView.entryAdapter?.sbn?.userId!!
+ } else {
+ userId = expandView.entry.sbn.userId
+ }
}
var fullShadeNeedsBouncer =
(!lockScreenUserManager.shouldShowLockscreenNotifications() ||
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/layout/LetterboxBackgroundProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/layout/LetterboxBackgroundProvider.kt
index 3d8ced1..a1536e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/layout/LetterboxBackgroundProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/layout/LetterboxBackgroundProvider.kt
@@ -23,7 +23,6 @@
import android.os.RemoteException
import android.view.IWindowManager
import com.android.systemui.CoreStartable
-import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -40,7 +39,7 @@
@Background private val backgroundExecutor: Executor,
private val wallpaperManager: WallpaperManager,
@Main private val mainHandler: Handler,
-) : CoreStartable, Dumpable {
+) : CoreStartable {
@ColorInt
var letterboxBackgroundColor: Int = Color.BLACK
private set
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 f52b924..df8fb5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
@@ -91,7 +91,7 @@
ImageSpan(it, ImageSpan.ALIGN_CENTER)
}
val decoratedSummary =
- SpannableString("x" + entry.ranking.summarization).apply {
+ SpannableString("x " + entry.ranking.summarization).apply {
setSpan(
/* what = */ imageSpan,
/* start = */ 0,
@@ -100,9 +100,9 @@
)
entry.ranking.summarization?.let {
setSpan(
- /* what = */ StyleSpan(Typeface.BOLD),
- /* start = */ 1,
- /* end = */ it.length,
+ /* what = */ StyleSpan(Typeface.ITALIC),
+ /* start = */ 2,
+ /* end = */ it.length + 2,
/* flags = */ Spanned.SPAN_EXCLUSIVE_INCLUSIVE,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
index ccfb43e..4053d06 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
@@ -25,6 +25,7 @@
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import com.android.wm.shell.bubbles.Bubbles;
import java.util.Optional;
@@ -99,8 +100,14 @@
row.setJustClicked(true);
DejankUtils.postAfterTraversal(() -> row.setJustClicked(false));
- if (!row.getEntry().isBubble() && mBubblesOptional.isPresent()) {
- mBubblesOptional.get().collapseStack();
+ if (NotificationBundleUi.isEnabled()) {
+ if (!row.getEntryAdapter().isBubbleCapable() && mBubblesOptional.isPresent()) {
+ mBubblesOptional.get().collapseStack();
+ }
+ } else {
+ if (!row.getEntry().isBubble() && mBubblesOptional.isPresent()) {
+ mBubblesOptional.get().collapseStack();
+ }
}
mNotificationActivityStarter.onNotificationClicked(entry, row);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PhysicsPropertyAnimator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PhysicsPropertyAnimator.kt
index 74faf25..0a24d7a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PhysicsPropertyAnimator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PhysicsPropertyAnimator.kt
@@ -15,12 +15,12 @@
*/
package com.android.systemui.statusbar.notification
+import android.util.FloatProperty
import android.util.Property
import android.view.View
-import androidx.dynamicanimation.animation.DynamicAnimation
-import androidx.dynamicanimation.animation.FloatPropertyCompat
-import androidx.dynamicanimation.animation.SpringAnimation
-import androidx.dynamicanimation.animation.SpringForce
+import com.android.internal.dynamicanimation.animation.DynamicAnimation
+import com.android.internal.dynamicanimation.animation.SpringAnimation
+import com.android.internal.dynamicanimation.animation.SpringForce
import com.android.systemui.res.R
import com.android.systemui.statusbar.notification.PhysicsPropertyAnimator.Companion.createDefaultSpring
import com.android.systemui.statusbar.notification.stack.AnimationProperties
@@ -33,8 +33,8 @@
*/
data class PhysicsProperty(val tag: Int, val property: Property<View, Float>) {
val offsetProperty =
- object : FloatPropertyCompat<View>(property.name) {
- override fun getValue(view: View): Float {
+ object : FloatProperty<View>(property.name) {
+ override fun get(view: View): Float {
return property.get(view)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java
index c79cae7..6dd44a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java
@@ -24,6 +24,7 @@
import android.app.Notification;
import android.content.Context;
import android.os.Build;
+import android.service.notification.StatusBarNotification;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -163,6 +164,48 @@
// TODO(b/396446620): implement bundle icons
return null;
}
+
+ @Override
+ public boolean isColorized() {
+ return false;
+ }
+
+ @Override
+ @Nullable
+ public StatusBarNotification getSbn() {
+ return null;
+ }
+
+ @Override
+ public boolean canDragAndDrop() {
+ return false;
+ }
+
+ @Override
+ public boolean isBubbleCapable() {
+ return false;
+ }
+
+ @Override
+ @Nullable
+ public String getStyle() {
+ return null;
+ }
+
+ @Override
+ public int getSectionBucket() {
+ return mBucket;
+ }
+
+ @Override
+ public boolean isAmbient() {
+ return false;
+ }
+
+ @Override
+ public boolean isFullScreenCapable() {
+ return false;
+ }
}
public static final List<BundleEntry> ROOT_BUNDLES = List.of(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
index 109ebe6..307a957 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.collection;
import android.content.Context;
+import android.service.notification.StatusBarNotification;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -109,4 +110,30 @@
* Returns whether the content of this entry is sensitive
*/
StateFlow<Boolean> isSensitive();
+
+ /**
+ * Returns whether this row has a background color set by an app
+ */
+ boolean isColorized();
+
+ /**
+ * Returns the SBN that backs this row, if present
+ */
+ @Nullable
+ StatusBarNotification getSbn();
+
+ boolean canDragAndDrop();
+
+ boolean isBubbleCapable();
+
+ @Nullable String getStyle();
+
+ int getSectionBucket();
+
+ boolean isAmbient();
+
+ default boolean isFullScreenCapable() {
+ return false;
+ }
}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index fb2a66c..b19ba3a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -32,7 +32,6 @@
import static com.android.systemui.statusbar.notification.collection.BundleEntry.ROOT_BUNDLES;
import static com.android.systemui.statusbar.notification.collection.GroupEntry.ROOT_ENTRY;
import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_NOT_CANCELED;
-import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_ALERTING;
import static java.util.Objects.requireNonNull;
@@ -42,6 +41,7 @@
import android.app.Notification.MessagingStyle.Message;
import android.app.NotificationChannel;
import android.app.NotificationManager.Policy;
+import android.app.PendingIntent;
import android.app.Person;
import android.app.RemoteInput;
import android.app.RemoteInputHistoryItem;
@@ -79,7 +79,6 @@
import com.android.systemui.statusbar.notification.row.shared.NotificationContentModel;
import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor;
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
-import com.android.systemui.statusbar.notification.stack.PriorityBucket;
import com.android.systemui.util.ListenerSet;
import kotlinx.coroutines.flow.MutableStateFlow;
@@ -183,7 +182,6 @@
new ListenerSet<>();
private boolean mPulseSupressed;
- private int mBucket = BUCKET_ALERTING;
private boolean mIsMarkedForUserTriggeredMovement;
private boolean mIsHeadsUpEntry;
@@ -353,6 +351,56 @@
public IconPack getIcons() {
return NotificationEntry.this.getIcons();
}
+
+ @Override
+ public boolean isColorized() {
+ return getSbn().getNotification().isColorized();
+ }
+
+ @Override
+ @Nullable
+ public StatusBarNotification getSbn() {
+ return NotificationEntry.this.getSbn();
+ }
+
+ @Override
+ public boolean canDragAndDrop() {
+ boolean canBubble = canBubble();
+ Notification notif = getSbn().getNotification();
+ PendingIntent dragIntent = notif.contentIntent != null ? notif.contentIntent
+ : notif.fullScreenIntent;
+ if (dragIntent != null && dragIntent.isActivity() && !canBubble) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isBubbleCapable() {
+ return NotificationEntry.this.isBubble();
+ }
+
+ @Override
+ @Nullable
+ public String getStyle() {
+ return getNotificationStyle();
+ }
+
+ @Override
+ public int getSectionBucket() {
+ return mBucket;
+ }
+
+ @Override
+ public boolean isAmbient() {
+ return mRanking.isAmbient();
+ }
+
+ @Override
+ public boolean isFullScreenCapable() {
+ return getSbn().getNotification().fullScreenIntent != null;
+ }
+
}
public EntryAdapter getEntryAdapter() {
@@ -560,15 +608,6 @@
return wasBubble != isBubble();
}
- @PriorityBucket
- public int getBucket() {
- return mBucket;
- }
-
- public void setBucket(@PriorityBucket int bucket) {
- mBucket = bucket;
- }
-
public ExpandableNotificationRow getRow() {
return row;
}
@@ -589,25 +628,45 @@
/**
* Get the children that are actually attached to this notification's row.
*
- * TODO: Seems like most callers here should probably be using
- * {@link GroupMembershipManager#getChildren(PipelineEntry)}
+ * TODO: Seems like most callers here should be asking a PipelineEntry, not a NotificationEntry
*/
public @Nullable List<NotificationEntry> getAttachedNotifChildren() {
- if (row == null) {
- return null;
+ if (NotificationBundleUi.isEnabled()) {
+ if (isGroupSummary()) {
+ return ((GroupEntry) getParent()).getChildren();
+ }
+ } else {
+ if (row == null) {
+ return null;
+ }
+
+ List<ExpandableNotificationRow> rowChildren = row.getAttachedChildren();
+ if (rowChildren == null) {
+ return null;
+ }
+
+ ArrayList<NotificationEntry> children = new ArrayList<>();
+ for (ExpandableNotificationRow child : rowChildren) {
+ children.add(child.getEntry());
+ }
+
+ return children;
+ }
+ return null;
+ }
+
+ private boolean isGroupSummary() {
+ if (getParent() == null) {
+ // The entry is not attached, so it doesn't count.
+ return false;
+ }
+ PipelineEntry pipelineEntry = getParent();
+ if (!(pipelineEntry instanceof GroupEntry groupEntry)) {
+ return false;
}
- List<ExpandableNotificationRow> rowChildren = row.getAttachedChildren();
- if (rowChildren == null) {
- return null;
- }
-
- ArrayList<NotificationEntry> children = new ArrayList<>();
- for (ExpandableNotificationRow child : rowChildren) {
- children.add(child.getEntry());
- }
-
- return children;
+ // If entry is a summary, its parent is a GroupEntry with summary = entry.
+ return groupEntry.getSummary() == this;
}
public void notifyFullScreenIntentLaunched() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java
index 78652cc..84de77b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java
@@ -16,10 +16,13 @@
package com.android.systemui.statusbar.notification.collection;
+import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_ALERTING;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection;
+import com.android.systemui.statusbar.notification.stack.PriorityBucket;
/**
* Class to represent a notification, group, or bundle in the pipeline.
@@ -29,6 +32,7 @@
final String mKey;
final ListAttachState mAttachState = ListAttachState.create();
final ListAttachState mPreviousAttachState = ListAttachState.create();
+ protected int mBucket = BUCKET_ALERTING;
public PipelineEntry(String key) {
this.mKey = key;
@@ -86,4 +90,13 @@
final ListAttachState getPreviousAttachState() {
return mPreviousAttachState;
}
+
+ @PriorityBucket
+ public int getBucket() {
+ return mBucket;
+ }
+
+ public void setBucket(@PriorityBucket int bucket) {
+ mBucket = bucket;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
index 248b528..28923b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.collection.coordinator
+import android.app.NotificationChannel.SYSTEM_RESERVED_IDS
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
@@ -89,6 +90,7 @@
object : NotifSectioner("Priority People", BUCKET_PRIORITY_PEOPLE) {
override fun isInSection(entry: PipelineEntry): Boolean {
return getPeopleType(entry) == TYPE_IMPORTANT_PERSON
+ && entry.representativeEntry?.channel?.id !in SYSTEM_RESERVED_IDS
}
}
@@ -96,10 +98,12 @@
val peopleAlertingSectioner = object : NotifSectioner("People(alerting)", BUCKET_PEOPLE) {
override fun isInSection(entry: PipelineEntry): Boolean {
if (SortBySectionTimeFlag.isEnabled) {
- return highPriorityProvider.isHighPriorityConversation(entry)
- || isConversation(entry)
+ return (highPriorityProvider.isHighPriorityConversation(entry)
+ || isConversation(entry))
+ && entry.representativeEntry?.channel?.id !in SYSTEM_RESERVED_IDS
} else {
return highPriorityProvider.isHighPriorityConversation(entry)
+ && entry.representativeEntry?.channel?.id !in SYSTEM_RESERVED_IDS
}
}
@@ -111,11 +115,12 @@
}
val peopleSilentSectioner = object : NotifSectioner("People(silent)", BUCKET_PEOPLE) {
- // Because the peopleAlertingSectioner is above this one, it will claim all conversations that are alerting.
- // All remaining conversations must be silent.
+ // Because the peopleAlertingSectioner is above this one, it will claim all conversations
+ // that are alerting. All remaining conversations must be silent.
override fun isInSection(entry: PipelineEntry): Boolean {
SortBySectionTimeFlag.assertInLegacyMode()
return isConversation(entry)
+ && entry.representativeEntry?.channel?.id !in SYSTEM_RESERVED_IDS
}
override fun getComparator(): NotifComparator {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpAnimator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpAnimator.kt
new file mode 100644
index 0000000..f9bd805
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpAnimator.kt
@@ -0,0 +1,63 @@
+/*
+ * 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.headsup
+
+import android.content.Context
+import com.android.systemui.res.R
+
+/**
+ * A class shared between [StackScrollAlgorithm] and [StackStateAnimator] to ensure all heads up
+ * animations use the same animation values.
+ */
+class HeadsUpAnimator(context: Context) {
+ init {
+ NotificationsHunSharedAnimationValues.assertInNewMode()
+ }
+
+ var headsUpAppearHeightBottom: Int = 0
+ var stackTopMargin: Int = 0
+
+ private var headsUpAppearStartAboveScreen = context.fetchHeadsUpAppearStartAboveScreen()
+
+ /**
+ * Returns the Y translation for a heads-up notification animation.
+ *
+ * For an appear animation, the returned Y translation should be the starting value of the
+ * animation. For a disappear animation, the returned Y translation should be the ending value
+ * of the animation.
+ */
+ fun getHeadsUpYTranslation(isHeadsUpFromBottom: Boolean): Int {
+ NotificationsHunSharedAnimationValues.assertInNewMode()
+
+ if (isHeadsUpFromBottom) {
+ // start from or end at the bottom of the screen
+ return headsUpAppearHeightBottom + headsUpAppearStartAboveScreen
+ }
+
+ // start from or end at the top of the screen
+ return -stackTopMargin - headsUpAppearStartAboveScreen
+ }
+
+ /** Should be invoked when resource values may have changed. */
+ fun updateResources(context: Context) {
+ headsUpAppearStartAboveScreen = context.fetchHeadsUpAppearStartAboveScreen()
+ }
+
+ private fun Context.fetchHeadsUpAppearStartAboveScreen(): Int {
+ return this.resources.getDimensionPixelSize(R.dimen.heads_up_appear_y_above_screen)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/NotificationsHunSharedAnimationValues.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/NotificationsHunSharedAnimationValues.kt
new file mode 100644
index 0000000..ca9d498
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/NotificationsHunSharedAnimationValues.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.headsup
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the notifications hun shared animation values flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object NotificationsHunSharedAnimationValues {
+ /** The aconfig flag name */
+ const val FLAG_NAME = Flags.FLAG_NOTIFICATIONS_HUN_SHARED_ANIMATION_VALUES
+
+ /** A token used for dependency declaration */
+ val token: FlagToken
+ get() = FlagToken(FLAG_NAME, isEnabled)
+
+ /** Is the refactor enabled */
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.notificationsHunSharedAnimationValues()
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This protects users from the
+ * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+ * build to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun isUnexpectedlyInLegacyMode() =
+ RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This will throw an exception if
+ * the flag is not enabled to ensure that the refactor author catches issues in testing.
+ * Caution!! Using this check incorrectly will cause crashes in nextfood builds!
+ */
+ @JvmStatic
+ inline fun assertInNewMode() = RefactorFlagUtils.assertInNewMode(isEnabled, FLAG_NAME)
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is enabled to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
index 7959e99..2a01a14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
@@ -20,6 +20,7 @@
import android.app.Flags.notificationsRedesignTemplates
import android.app.Notification
import android.graphics.PorterDuff
+import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.View.GONE
@@ -80,9 +81,13 @@
val content = viewModel.content ?: return
val audiblyAlertedIconVisible = viewModel.audiblyAlertedIconVisible
- key(content.identity) {
- val layoutResource = content.layoutResource ?: return
+ val layoutResource = content.layoutResource
+ if (layoutResource == null) {
+ Log.w(TAG, "not displaying promoted notif with ineligible style on AOD")
+ return
+ }
+ key(content.identity) {
val sidePaddings = dimensionResource(systemuiR.dimen.notification_side_paddings)
val sidePaddingValues = PaddingValues(horizontal = sidePaddings, vertical = 0.dp)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index a081ad5..6837cb2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -119,6 +119,7 @@
protected Point mTargetPoint;
private boolean mDismissed;
private boolean mRefocusOnDismiss;
+ protected boolean mIsBlurSupported;
public ActivatableNotificationView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -128,12 +129,13 @@
}
private void updateColors() {
- if (notificationRowTransparency()) {
+ if (usesTransparentBackground()) {
mNormalColor = SurfaceEffectColors.surfaceEffect1(getContext());
} else {
mNormalColor = mContext.getColor(
com.android.internal.R.color.materialColorSurfaceContainerHigh);
}
+ setBackgroundToNormalColor();
mTintedRippleColor = mContext.getColor(
R.color.notification_ripple_tinted_color);
mNormalRippleColor = mContext.getColor(
@@ -144,6 +146,12 @@
mOverrideAmount = 0.0f;
}
+ private void setBackgroundToNormalColor() {
+ if (mBackgroundNormal != null) {
+ mBackgroundNormal.setNormalColor(mNormalColor);
+ }
+ }
+
/**
* Reload background colors from resources and invalidate views.
*/
@@ -173,6 +181,7 @@
mBackgroundNormal = findViewById(R.id.backgroundNormal);
mFakeShadow = findViewById(R.id.fake_shadow);
mShadowHidden = mFakeShadow.getVisibility() != VISIBLE;
+ setBackgroundToNormalColor();
initBackground();
updateBackgroundTint();
updateOutlineAlpha();
@@ -326,6 +335,21 @@
mBackgroundNormal.setBottomAmountClips(!isChildInGroup());
}
+ public void setIsBlurSupported(boolean isBlurSupported) {
+ if (!notificationRowTransparency()) {
+ return;
+ }
+ boolean usedTransparentBackground = usesTransparentBackground();
+ mIsBlurSupported = isBlurSupported;
+ if (usedTransparentBackground != usesTransparentBackground()) {
+ updateBackgroundColors();
+ }
+ }
+
+ protected boolean usesTransparentBackground() {
+ return mIsBlurSupported && notificationRowTransparency();
+ }
+
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
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 35e84e5..3ef1fd2 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
@@ -20,8 +20,8 @@
import static android.app.Notification.Action.SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
-import static com.android.systemui.Flags.notificationsPinnedHunInShade;
import static com.android.systemui.Flags.notificationRowTransparency;
+import static com.android.systemui.Flags.notificationsPinnedHunInShade;
import static com.android.systemui.flags.Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE;
import static com.android.systemui.statusbar.notification.NotificationUtils.logKey;
import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.PARENT_DISMISSED;
@@ -1678,11 +1678,17 @@
if (view != null) {
view.setBackgroundTintColor(color);
}
- if (notificationRowTransparency()
- && (mBackgroundNormal != null)
- && (mEntry != null)) {
- mBackgroundNormal.setBgIsColorized(
- mEntry.getSbn().getNotification().isColorized());
+ if (notificationRowTransparency() && mBackgroundNormal != null) {
+ if (NotificationBundleUi.isEnabled()) {
+ mBackgroundNormal.setBgIsColorized(
+ usesTransparentBackground() && mEntryAdapter.isColorized());
+ } else {
+ if (mEntry != null) {
+ mBackgroundNormal.setBgIsColorized(
+ usesTransparentBackground()
+ && mEntry.getSbn().getNotification().isColorized());
+ }
+ }
}
}
@@ -2374,7 +2380,11 @@
return traceTag;
}
- return traceTag + "(" + getEntry().getNotificationStyle() + ")";
+ if (NotificationBundleUi.isEnabled()) {
+ return traceTag + "(" + getEntryAdapter().getStyle() + ")";
+ } else {
+ return traceTag + "(" + getEntry().getNotificationStyle() + ")";
+ }
}
@Override
@@ -3012,6 +3022,7 @@
mUserLocked = userLocked;
mPrivateLayout.setUserExpanding(userLocked);
+ mPublicLayout.setUserExpanding(userLocked);
// This is intentionally not guarded with mIsSummaryWithChildren since we might have had
// children but not anymore.
if (mChildrenContainer != null) {
@@ -3698,8 +3709,14 @@
return true;
}
// The colorized background is another layer with which all other elements overlap
- if (getEntry().getSbn().getNotification().isColorized()) {
- return true;
+ if (NotificationBundleUi.isEnabled()) {
+ if (mEntryAdapter.isColorized()) {
+ return true;
+ }
+ } else {
+ if (getEntry().getSbn().getNotification().isColorized()) {
+ return true;
+ }
}
// Check if the showing layout has a need for overlapping rendering.
// NOTE: We could check both public and private layouts here, but becuause these states
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index 07711b6..02e8f49 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -127,7 +127,14 @@
@Override
public void onSettingChanged(Uri setting, int userId, String value) {
if (BUBBLES_SETTING_URI.equals(setting)) {
- final int viewUserId = mView.getEntry().getSbn().getUserId();
+ if (NotificationBundleUi.isEnabled()
+ && mView.getEntryAdapter().getSbn() == null) {
+ // only valid for notification rows
+ return;
+ }
+ final int viewUserId = NotificationBundleUi.isEnabled()
+ ? mView.getEntryAdapter().getSbn().getUserId()
+ : mView.getEntry().getSbn().getUserId();
if (viewUserId == UserHandle.USER_ALL || viewUserId == userId) {
mView.getPrivateLayout().setBubblesEnabledForUser(
BUBBLES_SETTING_ENABLED_VALUE.equals(value));
@@ -376,8 +383,12 @@
public void onViewAttachedToWindow(View v) {
if (NotificationBundleUi.isEnabled()) {
mView.setInitializationTime(mClock.elapsedRealtime());
+ if (mView.getEntryAdapter().getSbn() != null) {
+ mSettingsController.addCallback(BUBBLES_SETTING_URI, mSettingsListener);
+ }
} else {
mView.getEntry().setInitializationTime(mClock.elapsedRealtime());
+ mSettingsController.addCallback(BUBBLES_SETTING_URI, mSettingsListener);
}
mPluginManager.addPluginListener(mView,
NotificationMenuRowPlugin.class, false /* Allow multiple */);
@@ -385,7 +396,7 @@
mView.setOnKeyguard(mStatusBarStateController.getState() == KEYGUARD);
mStatusBarStateController.addCallback(mStatusBarStateListener);
}
- mSettingsController.addCallback(BUBBLES_SETTING_URI, mSettingsListener);
+
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
index d5551b1..9ae2eb1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
@@ -54,6 +54,7 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger;
import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import javax.inject.Inject;
@@ -100,7 +101,15 @@
enr = (ExpandableNotificationRow) view;
}
- StatusBarNotification sn = enr.getEntry().getSbn();
+ if (NotificationBundleUi.isEnabled()) {
+ if (!enr.getEntryAdapter().canDragAndDrop()) {
+ return;
+ }
+ }
+
+ StatusBarNotification sn = NotificationBundleUi.isEnabled()
+ ? enr.getEntryAdapter().getSbn()
+ : enr.getEntry().getSbn();
Notification notification = sn.getNotification();
final PendingIntent contentIntent = notification.contentIntent != null
? notification.contentIntent
@@ -115,8 +124,7 @@
.show();
return;
}
- Bitmap iconBitmap = getBitmapFromDrawable(
- getPkgIcon(enr.getEntry().getSbn().getPackageName()));
+ Bitmap iconBitmap = getBitmapFromDrawable(getPkgIcon(sn.getPackageName()));
final ImageView snapshot = new ImageView(mContext);
snapshot.setImageBitmap(iconBitmap);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
index 344d0f6..ba80f01 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
@@ -51,6 +51,7 @@
*/
public class HybridConversationNotificationView extends HybridNotificationView {
+ private static final int MAX_SUMMARIZATION_LINES = 2;
private ImageView mConversationIconView;
private TextView mConversationSenderName;
private ViewStub mConversationFacePileStub;
@@ -292,11 +293,14 @@
@Nullable CharSequence summarization
) {
if (AsyncHybridViewInflation.isUnexpectedlyInLegacyMode()) return;
- if (summarization != null) {
+ if (!TextUtils.isEmpty(summarization)) {
mConversationSenderName.setVisibility(GONE);
titleText = null;
contentText = summarization;
+ mTextView.setSingleLine(false);
+ mTextView.setMaxLines(MAX_SUMMARIZATION_LINES);
} else {
+ mTextView.setSingleLine(true);
if (conversationSenderName == null) {
mConversationSenderName.setVisibility(GONE);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index 4978fa4..e1219e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -73,7 +73,7 @@
private int mDrawableAlpha = 255;
private final ColorStateList mLightColoredStatefulColors;
private final ColorStateList mDarkColoredStatefulColors;
- private final int mNormalColor;
+ private int mNormalColor;
private boolean mBgIsColorized = false;
private boolean mForceOpaque = false;
private final int convexR = 9;
@@ -89,15 +89,13 @@
R.color.notification_state_color_light);
mDarkColoredStatefulColors = getResources().getColorStateList(
R.color.notification_state_color_dark);
- if (notificationRowTransparency()) {
- mNormalColor = SurfaceEffectColors.surfaceEffect1(getContext());
- } else {
- mNormalColor = mContext.getColor(
- com.android.internal.R.color.materialColorSurfaceContainerHigh);
- }
mFocusOverlayStroke = getResources().getDimension(R.dimen.notification_focus_stroke_width);
}
+ public void setNormalColor(int color) {
+ mNormalColor = color;
+ }
+
@Override
public void onTargetVisibilityChanged(boolean targetVisible) {
if (NotificationAddXOnHoverToDismiss.isUnexpectedlyInLegacyMode()) {
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 7444679..daa598b 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
@@ -70,6 +70,7 @@
import com.android.systemui.statusbar.notification.row.ui.viewbinder.SingleLineViewBinder;
import com.android.systemui.statusbar.notification.row.ui.viewmodel.SingleLineViewModel;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
@@ -468,7 +469,10 @@
if (LockscreenOtpRedaction.isEnabled()
&& bindParams.redactionType == REDACTION_TYPE_SENSITIVE_CONTENT) {
result.newPublicView = createSensitiveContentMessageNotification(
- row.getEntry().getSbn().getNotification(), builder.getStyle(),
+ NotificationBundleUi.isEnabled()
+ ? row.getEntryAdapter().getSbn().getNotification()
+ : row.getEntry().getSbn().getNotification(),
+ builder.getStyle(),
systemUiContext, packageContext).createContentView();
} else {
result.newPublicView = builder.makePublicContentView(bindParams.isMinimized);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 0d29981..1932037 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -64,6 +64,7 @@
import com.android.systemui.statusbar.notification.row.wrapper.NotificationCustomViewWrapper;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationHeaderViewWrapper;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import com.android.systemui.statusbar.phone.ExpandHeadsUpOnInlineReply;
import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder;
@@ -594,12 +595,11 @@
if (mContainingNotification == null) {
return null;
}
- final NotificationEntry entry = mContainingNotification.getEntry();
- if (entry == null) {
- return null;
+ if (NotificationBundleUi.isEnabled()) {
+ return mContainingNotification.getEntryAdapter().getSbn();
+ } else {
+ return mContainingNotification.getEntry().getSbn();
}
-
- return entry.getSbn();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt
index bb4aa86..4082a5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt
@@ -26,6 +26,7 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.NotifRemoteViewsFactory
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
import javax.inject.Inject
/**
@@ -60,7 +61,18 @@
row: ExpandableNotificationRow,
context: Context,
): NotificationIconProvider {
- val sbn = row.entry.sbn
+ val sbn = if (NotificationBundleUi.isEnabled) row.entryAdapter?.sbn else row.entry.sbn
+ if (sbn == null) {
+ return object : NotificationIconProvider {
+ override fun shouldShowAppIcon(): Boolean {
+ return false
+ }
+
+ override fun getAppIcon(): Drawable? {
+ return null
+ }
+ }
+ }
return object : NotificationIconProvider {
override fun shouldShowAppIcon(): Boolean {
val shouldShowAppIcon = iconStyleProvider.shouldShowAppIcon(sbn, context)
@@ -68,7 +80,7 @@
return shouldShowAppIcon
}
- override fun getAppIcon(): Drawable {
+ override fun getAppIcon(): Drawable? {
val withWorkProfileBadge =
iconStyleProvider.shouldShowWorkProfileBadge(sbn, context)
return appIconProvider.getOrFetchAppIcon(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt
index f0b5c36..8984f2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt
@@ -50,6 +50,15 @@
view.registerListenersWhileAttached(touchHandler)
}
}
+ view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ launch {
+ viewModel.isBlurSupported.collect { supported ->
+ view.setIsBlurSupported(supported)
+ }
+ }
+ }
+ }
}
private suspend fun ActivatableNotificationView.registerListenersWhileAttached(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModel.kt
index f46d424..9a86ffb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.row.ui.viewmodel
import com.android.systemui.accessibility.domain.interactor.AccessibilityInteractor
+import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.flow.Flow
@@ -26,25 +27,34 @@
interface ActivatableNotificationViewModel : ExpandableOutlineViewModel {
/** Does the view react to touches? */
val isTouchable: Flow<Boolean>
+ val isBlurSupported: Flow<Boolean>
companion object {
operator fun invoke(
a11yInteractor: AccessibilityInteractor,
- ): ActivatableNotificationViewModel = ActivatableNotificationViewModelImpl(a11yInteractor)
+ windowRootViewBlurInteractor: WindowRootViewBlurInteractor
+ ): ActivatableNotificationViewModel =
+ ActivatableNotificationViewModelImpl(a11yInteractor, windowRootViewBlurInteractor)
}
}
private class ActivatableNotificationViewModelImpl(
a11yInteractor: AccessibilityInteractor,
+ windowRootViewBlurInteractor: WindowRootViewBlurInteractor,
) : ActivatableNotificationViewModel {
override val isTouchable: Flow<Boolean> =
// If a11y touch exploration is enabled, then the activatable view should ignore touches
a11yInteractor.isTouchExplorationEnabled.map { !it }
+ override val isBlurSupported: Flow<Boolean> =
+ windowRootViewBlurInteractor.isBlurCurrentlySupported
}
@Module
object ActivatableNotificationViewModelModule {
@Provides
- fun provideViewModel(interactor: AccessibilityInteractor) =
- ActivatableNotificationViewModel(interactor)
+ fun provideViewModel(
+ a11yInteractor: AccessibilityInteractor,
+ windowRootViewBlurInteractor: WindowRootViewBlurInteractor
+ ) =
+ ActivatableNotificationViewModel(a11yInteractor, windowRootViewBlurInteractor)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
index 990adf7..f492b25 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java
@@ -30,6 +30,7 @@
import com.android.systemui.statusbar.notification.ImageTransformState;
import com.android.systemui.statusbar.notification.row.BigPictureIconManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
/**
* Wraps a notification containing a big picture template
@@ -47,7 +48,9 @@
public void onContentUpdated(ExpandableNotificationRow row) {
super.onContentUpdated(row);
resolveViews();
- updateImageTag(row.getEntry().getSbn());
+ updateImageTag(NotificationBundleUi.isEnabled()
+ ? row.getEntryAdapter().getSbn()
+ : row.getEntry().getSbn());
}
private void resolveViews() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java
index d58c183..dec674c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java
@@ -23,6 +23,7 @@
import com.android.internal.widget.ImageFloatingTextView;
import com.android.systemui.statusbar.TransformableView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
/**
* Wraps a notification containing a big text template
@@ -44,7 +45,9 @@
public void onContentUpdated(ExpandableNotificationRow row) {
// Reinspect the notification. Before the super call, because the super call also updates
// the transformation types and we need to have our values set by then.
- resolveViews(row.getEntry().getSbn());
+ resolveViews(NotificationBundleUi.isEnabled()
+ ? row.getEntryAdapter().getSbn()
+ : row.getEntry().getSbn());
super.onContentUpdated(row);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index e9eecdd8..585051a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -52,6 +52,7 @@
import com.android.systemui.statusbar.notification.TransformState;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.shared.NotificationAddXOnHoverToDismiss;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import java.util.Stack;
@@ -222,7 +223,9 @@
@Override
public void onContentUpdated(ExpandableNotificationRow row) {
super.onContentUpdated(row);
- mIsLowPriority = row.getEntry().isAmbient();
+ mIsLowPriority = NotificationBundleUi.isEnabled()
+ ? row.getEntryAdapter().isAmbient()
+ : row.getEntry().isAmbient();
mTransformLowPriorityTitle = !row.isChildInGroup() && !row.isSummaryWithChildren();
ArraySet<View> previousViews = mTransformationHelper.getAllTransformingViews();
@@ -231,7 +234,9 @@
updateTransformedTypes();
addRemainingTransformTypes();
updateCropToPaddingForImageViews();
- Notification n = row.getEntry().getSbn().getNotification();
+ Notification n = NotificationBundleUi.isEnabled()
+ ? row.getEntryAdapter().getSbn().getNotification()
+ : row.getEntry().getSbn().getNotification();
mIcon.setTag(ImageTransformState.ICON_TAG, n.getSmallIcon());
// We need to reset all views that are no longer transforming in case a view was previously
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
index b9aa571..4146a94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
@@ -54,6 +54,7 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.HybridNotificationView;
import com.android.systemui.util.DimensionKt;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import java.util.function.Consumer;
@@ -310,7 +311,9 @@
public void onContentUpdated(ExpandableNotificationRow row) {
// Reinspect the notification. Before the super call, because the super call also updates
// the transformation types and we need to have our values set by then.
- resolveTemplateViews(row.getEntry().getSbn());
+ resolveTemplateViews(NotificationBundleUi.isEnabled()
+ ? row.getEntryAdapter().getSbn()
+ : row.getEntry().getSbn());
super.onContentUpdated(row);
// With the modern templates, a large icon visually overlaps the header, so we can't
// hide the header, we must show it.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 3987ca6..64babb2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -80,7 +80,10 @@
return new NotificationProgressTemplateViewWrapper(ctx, v, row);
}
- if (row.getEntry().getSbn().getNotification().isStyle(
+ if (NotificationBundleUi.isEnabled()
+ ? row.getEntryAdapter().getSbn().getNotification().isStyle(
+ Notification.DecoratedCustomViewStyle.class)
+ : row.getEntry().getSbn().getNotification().isStyle(
Notification.DecoratedCustomViewStyle.class)) {
return new NotificationDecoratedCustomViewWrapper(ctx, v, row);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java
index 3d60092..4d01cbd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java
@@ -23,7 +23,7 @@
import android.view.View;
import android.view.animation.Interpolator;
-import androidx.dynamicanimation.animation.DynamicAnimation;
+import com.android.internal.dynamicanimation.animation.DynamicAnimation;
import java.util.function.Consumer;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 048958e..e830d18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -60,6 +60,7 @@
import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationHeaderViewWrapper;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import java.util.ArrayList;
import java.util.List;
@@ -421,7 +422,12 @@
Trace.beginSection("NotifChildCont#recreateHeader");
mHeaderClickListener = listener;
mIsConversation = isConversation;
- StatusBarNotification notification = mContainingNotification.getEntry().getSbn();
+ StatusBarNotification notification = NotificationBundleUi.isEnabled()
+ ? mContainingNotification.getEntryAdapter().getSbn()
+ : mContainingNotification.getEntry().getSbn();
+ if (notification == null) {
+ return;
+ }
final Notification.Builder builder = Notification.Builder.recoverBuilder(getContext(),
notification.getNotification());
Trace.beginSection("recreateHeader#makeNotificationGroupHeader");
@@ -565,7 +571,12 @@
void recreateLowPriorityHeader(Notification.Builder builder) {
AsyncGroupHeaderViewInflation.assertInLegacyMode();
RemoteViews header;
- StatusBarNotification notification = mContainingNotification.getEntry().getSbn();
+ StatusBarNotification notification = NotificationBundleUi.isEnabled()
+ ? mContainingNotification.getEntryAdapter().getSbn()
+ : mContainingNotification.getEntry().getSbn();
+ if (notification == null) {
+ return;
+ }
if (mIsMinimized) {
if (builder == null) {
builder = Notification.Builder.recoverBuilder(getContext(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index 3d8fe01..96f0e6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -35,6 +35,7 @@
import com.android.systemui.statusbar.notification.dagger.SocialHeader
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
import com.android.systemui.statusbar.notification.stack.PriorityBucket
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.SectionProvider
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -164,7 +165,9 @@
view === socialHeaderView -> BUCKET_SOCIAL
view === recsHeaderView -> BUCKET_RECS
view === promoHeaderView -> BUCKET_PROMO
- view is ExpandableNotificationRow -> view.entry.bucket
+ view is ExpandableNotificationRow ->
+ if (NotificationBundleUi.isEnabled) view.entryAdapter?.sectionBucket
+ else view.entry.bucket
else -> null
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index e7a77eb..4390f1b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -117,8 +117,10 @@
import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix;
import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView;
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpAnimator;
import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper;
import com.android.systemui.statusbar.notification.headsup.HeadsUpUtil;
+import com.android.systemui.statusbar.notification.headsup.NotificationsHunSharedAnimationValues;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -233,6 +235,8 @@
private String mLastInitViewDumpString;
private long mLastInitViewElapsedRealtime;
+ @Nullable
+ private final HeadsUpAnimator mHeadsUpAnimator;
/**
* The algorithm which calculates the properties for our children
*/
@@ -668,8 +672,13 @@
mExpandHelper.setEventSource(this);
mExpandHelper.setScrollAdapter(mScrollAdapter);
- mStackScrollAlgorithm = createStackScrollAlgorithm(context);
- mStateAnimator = new StackStateAnimator(context, this);
+ if (NotificationsHunSharedAnimationValues.isEnabled()) {
+ mHeadsUpAnimator = new HeadsUpAnimator(context);
+ } else {
+ mHeadsUpAnimator = null;
+ }
+ mStackScrollAlgorithm = new StackScrollAlgorithm(context, this, mHeadsUpAnimator);
+ mStateAnimator = new StackStateAnimator(context, this, mHeadsUpAnimator);
setOutlineProvider(mOutlineProvider);
// We could set this whenever we 'requestChildUpdate' much like the viewTreeObserver, but
@@ -1024,12 +1033,18 @@
|| !(view instanceof ExpandableNotificationRow row)) {
continue;
}
+ int bucket = NotificationBundleUi.isEnabled()
+ ? row.getEntryAdapter().getSectionBucket()
+ : row.getEntry().getBucket();
+ boolean isAmbient = NotificationBundleUi.isEnabled()
+ ? row.getEntryAdapter().isAmbient()
+ : row.getEntry().isAmbient();
currentIndex++;
boolean beforeSpeedBump;
if (mHighPriorityBeforeSpeedBump) {
- beforeSpeedBump = row.getEntry().getBucket() < BUCKET_SILENT;
+ beforeSpeedBump = bucket < BUCKET_SILENT;
} else {
- beforeSpeedBump = !row.getEntry().isAmbient();
+ beforeSpeedBump = !isAmbient;
}
if (beforeSpeedBump) {
speedBumpIndex = currentIndex;
@@ -3582,10 +3597,6 @@
mGoToFullShadeNeedsAnimation = false;
}
- protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
- return new StackScrollAlgorithm(context, this);
- }
-
/**
* @return Whether a y coordinate is inside the content.
*/
@@ -5111,9 +5122,16 @@
public void setHeadsUpBoundaries(int height, int bottomBarHeight) {
SceneContainerFlag.assertInLegacyMode();
mAmbientState.setMaxHeadsUpTranslation(height - bottomBarHeight);
- mStackScrollAlgorithm.setHeadsUpAppearHeightBottom(height);
- mStateAnimator.setHeadsUpAppearHeightBottom(height);
- mStateAnimator.setStackTopMargin(mAmbientState.getStackTopMargin());
+
+ if (NotificationsHunSharedAnimationValues.isEnabled()) {
+ mHeadsUpAnimator.setHeadsUpAppearHeightBottom(height);
+ mHeadsUpAnimator.setStackTopMargin(mAmbientState.getStackTopMargin());
+ } else {
+ mStackScrollAlgorithm.setHeadsUpAppearHeightBottom(height);
+ mStateAnimator.setHeadsUpAppearHeightBottom(height);
+ mStateAnimator.setStackTopMargin(mAmbientState.getStackTopMargin());
+ }
+
requestChildrenUpdate();
}
@@ -6423,13 +6441,16 @@
static boolean matchesSelection(
ExpandableNotificationRow row,
@SelectedRows int selection) {
+ int bucket = NotificationBundleUi.isEnabled()
+ ? row.getEntryAdapter().getSectionBucket()
+ : row.getEntry().getBucket();
switch (selection) {
case ROWS_ALL:
return true;
case ROWS_HIGH_PRIORITY:
- return row.getEntry().getBucket() < BUCKET_SILENT;
+ return bucket < BUCKET_SILENT;
case ROWS_GENTLE:
- return row.getEntry().getBucket() == BUCKET_SILENT;
+ return bucket == BUCKET_SILENT;
default:
throw new IllegalArgumentException("Unknown selection: " + selection);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 4459430..124e6f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -122,6 +122,7 @@
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationSnooze;
import com.android.systemui.statusbar.notification.shared.GroupHunAnimationFix;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import com.android.systemui.statusbar.notification.stack.ui.viewbinder.NotificationListViewBinder;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -639,8 +640,10 @@
mView.onSwipeEnd();
if (animView instanceof ExpandableNotificationRow row) {
if (row.isPinned() && !canChildBeDismissed(row)
- && row.getEntry().getSbn().getNotification().fullScreenIntent
- == null) {
+ && NotificationBundleUi.isEnabled()
+ ? !row.getEntryAdapter().isFullScreenCapable()
+ : (row.getEntry().getSbn().getNotification().fullScreenIntent
+ == null)) {
mHeadsUpManager.removeNotification(
row.getKey(),
/* removeImmediately= */ true,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
index 08bc8f5..4e91680 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
@@ -32,6 +32,7 @@
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiForceExpanded
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
import com.android.systemui.statusbar.notification.shared.NotificationMinimalism
import com.android.systemui.statusbar.policy.SplitShadeStateController
import com.android.systemui.util.Compile
@@ -407,8 +408,14 @@
}
if (counter != null) {
- val entry = (currentNotification as? ExpandableNotificationRow)?.entry
- counter.incrementForBucket(entry?.bucket)
+ if (NotificationBundleUi.isEnabled) {
+ val entry = (currentNotification as? ExpandableNotificationRow)?.entry
+ counter.incrementForBucket(entry?.bucket)
+ } else {
+ val entryAdapter =
+ (currentNotification as? ExpandableNotificationRow)?.entryAdapter
+ counter.incrementForBucket(entryAdapter?.sectionBucket)
+ }
}
log {
@@ -461,12 +468,15 @@
val height = view.heightWithoutLockscreenConstraints.toFloat()
val gapAndDividerHeight =
calculateGapAndDividerHeight(stack, previousView, current = view, visibleIndex)
+ val canPeek = view is ExpandableNotificationRow &&
+ if (NotificationBundleUi.isEnabled) view.entryAdapter?.canPeek() == true
+ else view.entry.isStickyAndNotDemoted
var size =
if (onLockscreen) {
if (
view is ExpandableNotificationRow &&
- (view.entry.isStickyAndNotDemoted ||
+ (canPeek ||
(PromotedNotificationUiForceExpanded.isEnabled &&
view.isPromotedOngoing))
) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 88d3ad8..4effb76 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -36,9 +36,12 @@
import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView;
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpAnimator;
+import com.android.systemui.statusbar.notification.headsup.NotificationsHunSharedAnimationValues;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import com.android.systemui.statusbar.notification.shared.NotificationHeadsUpCycling;
import java.util.ArrayList;
@@ -56,6 +59,9 @@
private static final String TAG = "StackScrollAlgorithm";
private static final SourceType STACK_SCROLL_ALGO = SourceType.from("StackScrollAlgorithm");
private final ViewGroup mHostView;
+ @Nullable
+ private final HeadsUpAnimator mHeadsUpAnimator;
+
private float mPaddingBetweenElements;
private float mGapHeight;
private float mGapHeightOnLockscreen;
@@ -78,8 +84,12 @@
private int mHeadsUpAppearHeightBottom;
private int mHeadsUpCyclingPadding;
- public StackScrollAlgorithm(Context context, ViewGroup hostView) {
+ public StackScrollAlgorithm(
+ Context context,
+ ViewGroup hostView,
+ @Nullable HeadsUpAnimator headsUpAnimator) {
mHostView = hostView;
+ mHeadsUpAnimator = headsUpAnimator;
initView(context);
}
@@ -111,6 +121,9 @@
mQuickQsOffsetHeight = SystemBarUtils.getQuickQsOffsetHeight(context);
mSmallCornerRadius = res.getDimension(R.dimen.notification_corner_radius_small);
mLargeCornerRadius = res.getDimension(R.dimen.notification_corner_radius);
+ if (NotificationsHunSharedAnimationValues.isEnabled()) {
+ mHeadsUpAnimator.updateResources(context);
+ }
}
/**
@@ -250,6 +263,7 @@
}
public void setHeadsUpAppearHeightBottom(int headsUpAppearHeightBottom) {
+ NotificationsHunSharedAnimationValues.assertInLegacyMode();
mHeadsUpAppearHeightBottom = headsUpAppearHeightBottom;
}
@@ -911,7 +925,9 @@
if (SceneContainerFlag.isEnabled()) {
if (shouldHunBeVisibleWhenScrolled(row.isHeadsUp(),
childState.headsUpIsVisible, row.showingPulsing(),
- ambientState.isOnKeyguard(), row.getEntry().isStickyAndNotDemoted())) {
+ ambientState.isOnKeyguard(), NotificationBundleUi.isEnabled()
+ ? row.getEntryAdapter().canPeek()
+ : row.getEntry().isStickyAndNotDemoted())) {
// the height of this child before clamping it to the top
float unmodifiedChildHeight = childState.height;
clampHunToTop(
@@ -963,7 +979,9 @@
} else {
if (shouldHunBeVisibleWhenScrolled(row.mustStayOnScreen(),
childState.headsUpIsVisible, row.showingPulsing(),
- ambientState.isOnKeyguard(), row.getEntry().isStickyAndNotDemoted())) {
+ ambientState.isOnKeyguard(), NotificationBundleUi.isEnabled()
+ ? row.getEntryAdapter().canPeek()
+ : row.getEntry().isStickyAndNotDemoted())) {
// Ensure that the heads up is always visible even when scrolled off.
// NSSL y starts at top of screen in non-split-shade, but below the qs
// offset
@@ -1037,14 +1055,22 @@
childState.setYTranslation(inSpaceTranslation + extraTranslation);
cyclingInHunHeight = -1;
} else if (!ambientState.isDozing()) {
- if (shouldHunAppearFromBottom(ambientState, childState)) {
- // move to the bottom of the screen
- childState.setYTranslation(
- mHeadsUpAppearHeightBottom + mHeadsUpAppearStartAboveScreen);
+ boolean shouldHunAppearFromBottom =
+ shouldHunAppearFromBottom(ambientState, childState);
+ if (NotificationsHunSharedAnimationValues.isEnabled()) {
+ int yTranslation =
+ mHeadsUpAnimator.getHeadsUpYTranslation(shouldHunAppearFromBottom);
+ childState.setYTranslation(yTranslation);
} else {
- // move to the top of the screen
- childState.setYTranslation(-ambientState.getStackTopMargin()
- - mHeadsUpAppearStartAboveScreen);
+ if (shouldHunAppearFromBottom) {
+ // move to the bottom of the screen
+ childState.setYTranslation(
+ mHeadsUpAppearHeightBottom + mHeadsUpAppearStartAboveScreen);
+ } else {
+ // move to the top of the screen
+ childState.setYTranslation(-ambientState.getStackTopMargin()
+ - mHeadsUpAppearStartAboveScreen);
+ }
}
} else {
// Make sure row yTranslation is at maximum the HUN yTranslation,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index 4da418e..19abfa8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -27,18 +27,20 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.annotation.Nullable;
import android.content.Context;
import android.util.Property;
import android.view.View;
-import androidx.dynamicanimation.animation.DynamicAnimation;
-
import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.dynamicanimation.animation.DynamicAnimation;
import com.android.systemui.res.R;
import com.android.systemui.shared.clocks.AnimatableClockView;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.PhysicsPropertyAnimator;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpAnimator;
+import com.android.systemui.statusbar.notification.headsup.NotificationsHunSharedAnimationValues;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
@@ -83,6 +85,9 @@
private final ExpandableViewState mTmpState = new ExpandableViewState();
private final AnimationProperties mAnimationProperties;
public NotificationStackScrollLayout mHostLayout;
+ @Nullable
+ private final HeadsUpAnimator mHeadsUpAnimator;
+
private ArrayList<NotificationStackScrollLayout.AnimationEvent> mNewEvents =
new ArrayList<>();
private ArrayList<View> mNewAddChildren = new ArrayList<>();
@@ -104,8 +109,12 @@
private NotificationShelf mShelf;
private StackStateLogger mLogger;
- public StackStateAnimator(Context context, NotificationStackScrollLayout hostLayout) {
+ public StackStateAnimator(
+ Context context,
+ NotificationStackScrollLayout hostLayout,
+ @Nullable HeadsUpAnimator headsUpAnimator) {
mHostLayout = hostLayout;
+ mHeadsUpAnimator = headsUpAnimator;
initView(context);
mAnimationProperties = new AnimationProperties() {
@@ -543,7 +552,6 @@
mHeadsUpAppearChildren.add(changingView);
mTmpState.copyFrom(changingView.getViewState());
- // translate the HUN in from the top, or the bottom of the screen
mTmpState.setYTranslation(getHeadsUpYTranslationStart(event.headsUpFromBottom));
// set the height and the initial position
mTmpState.applyToView(changingView);
@@ -728,6 +736,10 @@
}
private float getHeadsUpYTranslationStart(boolean headsUpFromBottom) {
+ if (NotificationsHunSharedAnimationValues.isEnabled()) {
+ return mHeadsUpAnimator.getHeadsUpYTranslation(headsUpFromBottom);
+ }
+
if (headsUpFromBottom) {
// start from the bottom of the screen
return mHeadsUpAppearHeightBottom + mHeadsUpAppearStartAboveScreen;
@@ -814,10 +826,12 @@
}
public void setHeadsUpAppearHeightBottom(int headsUpAppearHeightBottom) {
+ NotificationsHunSharedAnimationValues.assertInLegacyMode();
mHeadsUpAppearHeightBottom = headsUpAppearHeightBottom;
}
public void setStackTopMargin(int stackTopMargin) {
+ NotificationsHunSharedAnimationValues.assertInLegacyMode();
mStackTopMargin = stackTopMargin;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
index 2ef6f36..29dbeb2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
@@ -30,10 +30,9 @@
import android.view.View;
import android.view.animation.Interpolator;
-import androidx.dynamicanimation.animation.DynamicAnimation;
-import androidx.dynamicanimation.animation.SpringAnimation;
-
import com.android.app.animation.Interpolators;
+import com.android.internal.dynamicanimation.animation.DynamicAnimation;
+import com.android.internal.dynamicanimation.animation.SpringAnimation;
import com.android.systemui.Dumpable;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.AnimatableProperty;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index 949cb0a..b662892 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -72,7 +72,7 @@
private val swipeStatusBarAwayGestureHandler: SwipeStatusBarAwayGestureHandler,
private val statusBarModeRepository: StatusBarModeRepositoryStore,
@OngoingCallLog private val logger: LogBuffer,
-) : CallbackController<OngoingCallListener>, Dumpable, CoreStartable {
+) : CallbackController<OngoingCallListener>, CoreStartable {
private var isFullscreen: Boolean = false
/** Non-null if there's an active call notification. */
private var callNotificationInfo: CallNotificationInfo? = null
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
index 3c53d2d..6355767 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
@@ -34,7 +34,6 @@
import androidx.annotation.VisibleForTesting
import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.systemui.CoreStartable
-import com.android.systemui.Dumpable
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -81,7 +80,7 @@
private val wakeLockBuilder: WakeLock.Builder,
private val systemClock: SystemClock,
internal val tempViewUiEventLogger: TemporaryViewUiEventLogger,
-) : CoreStartable, Dumpable {
+) : CoreStartable {
/**
* Window layout params that will be used as a starting point for the [windowLayoutParams] of
* all subclasses.
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperFocalAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperFocalAreaViewModel.kt
index 4cd49d0..1e78b12 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperFocalAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ui/viewmodel/WallpaperFocalAreaViewModel.kt
@@ -26,6 +26,7 @@
import javax.inject.Inject
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
class WallpaperFocalAreaViewModel
@@ -39,25 +40,31 @@
val wallpaperFocalAreaBounds =
combine(
wallpaperFocalAreaInteractor.wallpaperFocalAreaBounds,
+ keyguardTransitionInteractor.startedKeyguardTransitionStep,
+ // Emit transition state when FINISHED instead of STARTED to avoid race with
+ // wakingup command, causing layout change command not be received.
keyguardTransitionInteractor
.transition(
edge = Edge.create(to = Scenes.Lockscreen),
edgeWithoutSceneContainer = Edge.create(to = KeyguardState.LOCKSCREEN),
)
- .filter { transitionStep ->
- // Should not filter by TransitionState.STARTED, it may race with
- // wakingup command, causing layout change command not be received.
- transitionStep.transitionState == TransitionState.FINISHED
- },
- ::Pair,
+ .filter { it.transitionState == TransitionState.FINISHED },
+ ::Triple,
)
- .map { (bounds, _) -> bounds }
+ .map { (bounds, startedStep, _) ->
+ // Avoid sending wrong bounds when transitioning from LOCKSCREEN to GONE
+ if (
+ startedStep.to == KeyguardState.LOCKSCREEN &&
+ startedStep.from != KeyguardState.LOCKSCREEN
+ ) {
+ bounds
+ } else {
+ null
+ }
+ }
+ .filterNotNull()
fun setFocalAreaBounds(bounds: RectF) {
wallpaperFocalAreaInteractor.setFocalAreaBounds(bounds)
}
-
- fun setTapPosition(x: Float, y: Float) {
- wallpaperFocalAreaInteractor.setTapPosition(x, y)
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index b6c6347..0e68fce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -28,6 +28,7 @@
import android.graphics.Rect
import android.graphics.drawable.BitmapDrawable
import android.hardware.biometrics.BiometricFingerprintConstants
+import android.hardware.biometrics.BiometricPrompt
import android.hardware.biometrics.PromptContentItemBulletedText
import android.hardware.biometrics.PromptContentView
import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton
@@ -42,6 +43,7 @@
import android.view.HapticFeedbackConstants
import android.view.MotionEvent
import android.view.Surface
+import android.view.accessibility.accessibilityManager
import androidx.test.filters.SmallTest
import com.android.app.activityTaskManager
import com.android.keyguard.AuthInteractionProperties
@@ -200,6 +202,8 @@
overrideResource(R.dimen.biometric_dialog_face_icon_size, mockFaceIconSize)
kosmos.applicationContext = context
+ whenever(kosmos.accessibilityManager.getRecommendedTimeoutMillis(anyInt(), anyInt()))
+ .thenReturn(BiometricPrompt.HIDE_DIALOG_DELAY)
if (testCase.fingerprint?.isAnyUdfpsType == true) {
kosmos.authController = authController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
index e035a02..f394c80 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
@@ -16,6 +16,9 @@
package com.android.systemui.media.controls.ui.viewmodel
+import android.icu.text.MeasureFormat
+import android.icu.util.Measure
+import android.icu.util.MeasureUnit
import android.media.MediaMetadata
import android.media.session.MediaController
import android.media.session.MediaSession
@@ -34,6 +37,7 @@
import com.android.systemui.util.concurrency.FakeRepeatableExecutor
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
+import java.util.Locale
import org.junit.After
import org.junit.Before
import org.junit.Ignore
@@ -155,6 +159,7 @@
whenever(mockController.getPlaybackState()).thenReturn(state)
// WHEN the controller is updated
viewModel.updateController(mockController)
+ fakeExecutor.runNextReady()
// THEN the duration is extracted
assertThat(viewModel.progress.value!!.duration).isEqualTo(duration)
assertThat(viewModel.progress.value!!.enabled).isTrue()
@@ -173,6 +178,7 @@
whenever(mockController.getMetadata()).thenReturn(metadata)
// WHEN the controller is updated
viewModel.updateController(mockController)
+ fakeExecutor.runNextReady()
// THEN the duration is extracted
assertThat(viewModel.progress.value!!.duration).isEqualTo(duration)
assertThat(viewModel.progress.value!!.enabled).isFalse()
@@ -197,6 +203,7 @@
whenever(mockController.getPlaybackState()).thenReturn(state)
// WHEN the controller is updated
viewModel.updateController(mockController)
+ fakeExecutor.runNextReady()
// THEN the seek bar is disabled
assertThat(viewModel.progress.value!!.enabled).isFalse()
}
@@ -220,6 +227,7 @@
whenever(mockController.getPlaybackState()).thenReturn(state)
// WHEN the controller is updated
viewModel.updateController(mockController)
+ fakeExecutor.runNextReady()
// THEN the seek bar is disabled
assertThat(viewModel.progress.value!!.enabled).isFalse()
}
@@ -238,6 +246,7 @@
whenever(mockController.getPlaybackState()).thenReturn(state)
// WHEN the controller is updated
viewModel.updateController(mockController)
+ fakeExecutor.runNextReady()
// THEN the seek bar is disabled
assertThat(viewModel.progress.value!!.enabled).isFalse()
}
@@ -254,6 +263,7 @@
whenever(mockController.getPlaybackState()).thenReturn(state)
// WHEN the controller is updated
viewModel.updateController(mockController)
+ fakeExecutor.runNextReady()
// THEN elapsed time is captured
assertThat(viewModel.progress.value!!.elapsedTime).isEqualTo(200.toInt())
}
@@ -536,6 +546,7 @@
whenever(mockController.getPlaybackState()).thenReturn(state)
// WHEN the controller is updated
viewModel.updateController(mockController)
+ fakeExecutor.runNextReady()
// THEN a task is queued
assertThat(fakeExecutor.numPending()).isEqualTo(1)
}
@@ -551,6 +562,7 @@
whenever(mockController.getPlaybackState()).thenReturn(state)
// WHEN updated
viewModel.updateController(mockController)
+ fakeExecutor.runNextReady()
// THEN an update task is not queued
assertThat(fakeExecutor.numPending()).isEqualTo(0)
}
@@ -572,6 +584,7 @@
whenever(mockController.getPlaybackState()).thenReturn(state)
// WHEN updated
viewModel.updateController(mockController)
+ fakeExecutor.runNextReady()
// THEN an update task is queued
assertThat(fakeExecutor.numPending()).isEqualTo(1)
}
@@ -593,6 +606,7 @@
whenever(mockController.getPlaybackState()).thenReturn(state)
// WHEN updated
viewModel.updateController(mockController)
+ fakeExecutor.runNextReady()
// THEN an update task is not queued
assertThat(fakeExecutor.numPending()).isEqualTo(0)
}
@@ -719,6 +733,7 @@
}
whenever(mockController.getPlaybackState()).thenReturn(state)
viewModel.updateController(mockController)
+ fakeExecutor.runNextReady()
// WHEN start listening
viewModel.listening = true
// THEN an update task is queued
@@ -820,6 +835,7 @@
whenever(mockController.playbackState).thenReturn(state)
val captor = ArgumentCaptor.forClass(MediaController.Callback::class.java)
viewModel.updateController(mockController)
+ fakeExecutor.runNextReady()
verify(mockController).registerCallback(captor.capture())
assertThat(viewModel.progress.value!!.elapsedTime).isEqualTo(firstPosition.toInt())
@@ -831,8 +847,48 @@
build()
}
captor.value.onPlaybackStateChanged(secondState)
+ fakeExecutor.runNextReady()
// THEN then elapsed time should be updated
assertThat(viewModel.progress.value!!.elapsedTime).isEqualTo(secondPosition.toInt())
}
+
+ @Test
+ fun contentDescriptionUpdated() {
+ // When there is a duration and position
+ val duration = (1.5 * 60 * 60 * 1000).toLong()
+ val metadata =
+ MediaMetadata.Builder().run {
+ putLong(MediaMetadata.METADATA_KEY_DURATION, duration)
+ build()
+ }
+ whenever(mockController.getMetadata()).thenReturn(metadata)
+
+ val elapsedTime = 3000L
+ val state =
+ PlaybackState.Builder().run {
+ setState(PlaybackState.STATE_PLAYING, elapsedTime, 1f)
+ build()
+ }
+ whenever(mockController.getPlaybackState()).thenReturn(state)
+
+ viewModel.updateController(mockController)
+ fakeExecutor.runNextReady()
+
+ // Then the content description is set
+ val result = viewModel.progress.value!!
+
+ val expectedProgress =
+ MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE)
+ .formatMeasures(Measure(3, MeasureUnit.SECOND))
+ val expectedDuration =
+ MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE)
+ .formatMeasures(
+ Measure(1, MeasureUnit.HOUR),
+ Measure(30, MeasureUnit.MINUTE),
+ Measure(0, MeasureUnit.SECOND),
+ )
+ assertThat(result.durationDescription).isEqualTo(expectedDuration)
+ assertThat(result.elapsedTimeDescription).isEqualTo(expectedProgress)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 7101df1..2a58890 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -82,6 +82,7 @@
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.notification.collection.EntryAdapter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
@@ -732,6 +733,9 @@
NotificationEntry entry = mock(NotificationEntry.class);
when(row.getEntry()).thenReturn(entry);
when(entry.isAmbient()).thenReturn(false);
+ EntryAdapter entryAdapter = mock(EntryAdapter.class);
+ when(entryAdapter.isAmbient()).thenReturn(false);
+ when(row.getEntryAdapter()).thenReturn(entryAdapter);
mStackScroller.addContainerView(row);
// speed bump = 1
@@ -748,6 +752,9 @@
NotificationEntry entry = mock(NotificationEntry.class);
when(row.getEntry()).thenReturn(entry);
when(entry.isAmbient()).thenReturn(true);
+ EntryAdapter entryAdapter = mock(EntryAdapter.class);
+ when(entryAdapter.isAmbient()).thenReturn(true);
+ when(row.getEntryAdapter()).thenReturn(entryAdapter);
mStackScroller.addContainerView(row);
// speed bump is set to 0
@@ -764,6 +771,9 @@
NotificationEntry entry = mock(NotificationEntry.class);
when(row.getEntry()).thenReturn(entry);
when(entry.isAmbient()).thenReturn(false);
+ EntryAdapter entryAdapter = mock(EntryAdapter.class);
+ when(entryAdapter.isAmbient()).thenReturn(false);
+ when(row.getEntryAdapter()).thenReturn(entryAdapter);
mStackScroller.addContainerView(row);
// speed bump is 1
@@ -1377,6 +1387,9 @@
when(row.canViewBeCleared()).thenReturn(true);
when(row.getEntry()).thenReturn(entry);
when(entry.isClearable()).thenReturn(true);
+ EntryAdapter entryAdapter = mock(EntryAdapter.class);
+ when(entryAdapter.isClearable()).thenReturn(true);
+ when(row.getEntryAdapter()).thenReturn(entryAdapter);
return row;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 5e95825..8281132 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -135,6 +135,8 @@
import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
+import com.android.systemui.statusbar.notification.collection.GroupEntry;
+import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
@@ -151,6 +153,7 @@
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProviderTestUtil;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -1229,8 +1232,36 @@
}
@Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
public void testBubbleSummaryDismissal_suppressesSummaryAndBubbleFromShade() throws Exception {
// GIVEN a group summary with a bubble child
+ NotificationEntry groupedBubble = mNotificationTestHelper.createBubbleEntryInGroup();
+ GroupEntry groupSummary = mNotificationTestHelper.createGroupEntry(
+ 0, List.of(groupedBubble));
+ mEntryListener.onEntryAdded(groupedBubble);
+ when(mCommonNotifCollection.getEntry(groupedBubble.getKey()))
+ .thenReturn(groupedBubble);
+ assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getKey()));
+
+ // WHEN the summary is dismissed
+ mBubblesManager.handleDismissalInterception(groupSummary.getSummary());
+
+ // THEN the summary and bubbled child are suppressed from the shade
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ groupedBubble.getKey(),
+ groupedBubble.getSbn().getGroupKey()));
+ assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade(
+ groupedBubble.getKey(),
+ groupedBubble.getSbn().getGroupKey()));
+ assertTrue(mBubbleData.isSummarySuppressed(
+ groupSummary.getSummary().getSbn().getGroupKey()));
+ }
+
+ @Test
+ @DisableFlags(NotificationBundleUi.FLAG_NAME)
+ public void testBubbleSummaryDismissal_suppressesSummaryAndBubbleFromShade_rows()
+ throws Exception {
+ // GIVEN a group summary with a bubble child
ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
mEntryListener.onEntryAdded(groupedBubble.getEntry());
@@ -1253,8 +1284,33 @@
}
@Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
public void testAppRemovesSummary_removesAllBubbleChildren() throws Exception {
// GIVEN a group summary with a bubble child
+ NotificationEntry groupedBubble = mNotificationTestHelper.createBubbleEntryInGroup();
+ GroupEntry groupSummary = mNotificationTestHelper.createGroupEntry(
+ 0, List.of(groupedBubble));
+ mEntryListener.onEntryAdded(groupedBubble);
+ when(mCommonNotifCollection.getEntry(groupedBubble.getKey()))
+ .thenReturn(groupedBubble);
+ assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getKey()));
+
+ // GIVEN the summary is dismissed
+ mBubblesManager.handleDismissalInterception(groupSummary.getSummary());
+
+ // WHEN the summary is cancelled by the app
+ mEntryListener.onEntryRemoved(groupSummary.getSummary(), REASON_APP_CANCEL);
+
+ // THEN the summary and its children are removed from bubble data
+ assertFalse(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getKey()));
+ assertFalse(mBubbleData.isSummarySuppressed(
+ groupSummary.getSummary().getSbn().getGroupKey()));
+ }
+
+ @Test
+ @DisableFlags(NotificationBundleUi.FLAG_NAME)
+ public void testAppRemovesSummary_removesAllBubbleChildren_rows() throws Exception {
+ // GIVEN a group summary with a bubble child
ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
mEntryListener.onEntryAdded(groupedBubble.getEntry());
@@ -1276,9 +1332,52 @@
}
@Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
public void testSummaryDismissalMarksBubblesHiddenFromShadeAndDismissesNonBubbledChildren()
throws Exception {
// GIVEN a group summary with two (non-bubble) children and one bubble child
+ NotificationEntry groupedBubble = mNotificationTestHelper.createBubbleEntryInGroup();
+ GroupEntry groupSummary = mNotificationTestHelper.createGroupEntry(
+ 2, List.of(groupedBubble));
+ mEntryListener.onEntryAdded(groupedBubble);
+ when(mCommonNotifCollection.getEntry(groupedBubble.getKey()))
+ .thenReturn(groupedBubble);
+
+ // WHEN the summary is dismissed
+ mBubblesManager.handleDismissalInterception(groupSummary.getSummary());
+
+ // THEN only the NON-bubble children are dismissed
+ List<NotificationEntry> children = groupSummary.getChildren();
+ verify(mNotifCallback, times(1)).removeNotification(
+ eq(children.get(0)), any(), eq(REASON_GROUP_SUMMARY_CANCELED));
+ verify(mNotifCallback, times(1)).removeNotification(
+ eq(children.get(1)), any(), eq(REASON_GROUP_SUMMARY_CANCELED));
+ verify(mNotifCallback, never()).removeNotification(eq(groupedBubble),
+ any(), anyInt());
+
+ // THEN the bubble child still exists as a bubble and is suppressed from the shade
+ assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getKey()));
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ groupedBubble.getKey(),
+ groupedBubble.getSbn().getGroupKey()));
+ assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade(
+ groupedBubble.getKey(),
+ groupedBubble.getSbn().getGroupKey()));
+
+ // THEN the summary is also suppressed from the shade
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ groupSummary.getSummary().getKey(),
+ groupSummary.getSummary().getSbn().getGroupKey()));
+ assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade(
+ groupSummary.getSummary().getKey(),
+ groupSummary.getSummary().getSbn().getGroupKey()));
+ }
+
+ @Test
+ @DisableFlags(NotificationBundleUi.FLAG_NAME)
+ public void testSummaryDismissalMarksBubblesHiddenFromShadeAndDismissesNonBubbledChildren_row()
+ throws Exception {
+ // GIVEN a group summary with two (non-bubble) children and one bubble child
ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(2);
ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
mEntryListener.onEntryAdded(groupedBubble.getEntry());
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt
index 43b57de..d6b625b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelKosmos.kt
@@ -17,6 +17,7 @@
package com.android.systemui.biometrics.ui.viewmodel
import android.content.applicationContext
+import android.view.accessibility.accessibilityManager
import com.android.app.activityTaskManager
import com.android.launcher3.icons.IconProvider
import com.android.systemui.biometrics.domain.interactor.biometricStatusInteractor
@@ -27,7 +28,6 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.util.mockito.mock
-import org.mockito.Mockito.mock
val Kosmos.promptViewModel by Fixture {
PromptViewModel(
@@ -39,6 +39,7 @@
udfpsUtils = udfpsUtils,
iconProvider = iconProvider,
activityTaskManager = activityTaskManager,
+ accessibilityManager = accessibilityManager,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt
deleted file mode 100644
index 43d3eb7..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt
+++ /dev/null
@@ -1,39 +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.communal.ui.viewmodel
-
-import android.service.dream.dreamManager
-import com.android.internal.logging.uiEventLogger
-import com.android.systemui.communal.domain.interactor.communalPrefsInteractor
-import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testDispatcher
-import com.android.systemui.plugins.activityStarter
-import com.android.systemui.statusbar.policy.batteryController
-
-val Kosmos.communalToDreamButtonViewModel by
- Kosmos.Fixture {
- CommunalToDreamButtonViewModel(
- backgroundContext = testDispatcher,
- batteryController = batteryController,
- prefsInteractor = communalPrefsInteractor,
- settingsInteractor = communalSettingsInteractor,
- activityStarter = activityStarter,
- dreamManager = dreamManager,
- uiEventLogger = uiEventLogger,
- )
- }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
index a64fc24..d6f0e06 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
@@ -104,6 +104,8 @@
private val _displayChangeEvent = MutableSharedFlow<Int>(replay = 1)
override val displayChangeEvent: Flow<Int> = _displayChangeEvent
+ override val displayIdsWithSystemDecorations: StateFlow<Set<Int>> = MutableStateFlow(emptySet())
+
suspend fun emitDisplayChangeEvent(displayId: Int) = _displayChangeEvent.emit(displayId)
fun setDefaultDisplayOff(defaultDisplayOff: Boolean) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/VisibilityLocationProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/VisibilityLocationProviderKosmos.kt
new file mode 100644
index 0000000..085d386
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/VisibilityLocationProviderKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * 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 org.mockito.kotlin.mock
+
+val Kosmos.visibilityLocationProvider: VisibilityLocationProvider by Kosmos.Fixture { mock() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorKosmos.kt
index 358d251..1a5c61a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorKosmos.kt
@@ -16,7 +16,41 @@
package com.android.systemui.statusbar.notification.collection.coordinator
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
+import com.android.systemui.concurrency.fakeExecutor
+import com.android.systemui.dump.dumpManager
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.keyguard.wakefulnessLifecycle
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.statusbar.statusBarStateController
+import com.android.systemui.shade.domain.interactor.shadeAnimationInteractor
+import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.statusbar.notification.collection.provider.visualStabilityProvider
+import com.android.systemui.statusbar.notification.domain.interactor.seenNotificationsInteractor
+import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository
+import com.android.systemui.statusbar.notification.visibilityLocationProvider
+import com.android.systemui.statusbar.policy.keyguardStateController
+import com.android.systemui.util.kotlin.JavaAdapter
-var Kosmos.visualStabilityCoordinator by Kosmos.Fixture { mock<VisualStabilityCoordinator>() }
+var Kosmos.visualStabilityCoordinator: VisualStabilityCoordinator by
+ Kosmos.Fixture {
+ VisualStabilityCoordinator(
+ fakeExecutor,
+ fakeExecutor,
+ dumpManager,
+ headsUpNotificationRepository,
+ shadeAnimationInteractor,
+ JavaAdapter(testScope.backgroundScope),
+ seenNotificationsInteractor,
+ statusBarStateController,
+ visibilityLocationProvider,
+ visualStabilityProvider,
+ wakefulnessLifecycle,
+ communalSceneInteractor,
+ shadeInteractor,
+ keyguardTransitionInteractor,
+ keyguardStateController,
+ visualStabilityCoordinatorLogger,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorLoggerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorLoggerKosmos.kt
new file mode 100644
index 0000000..6645cdc
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorLoggerKosmos.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.collection.coordinator
+
+import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
+
+val Kosmos.visualStabilityCoordinatorLogger: VisualStabilityCoordinatorLogger by
+ Kosmos.Fixture { mock() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelKosmos.kt
index 2523975..7ccbdb7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelKosmos.kt
@@ -19,9 +19,11 @@
import com.android.systemui.accessibility.domain.interactor.accessibilityInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.window.domain.interactor.windowRootViewBlurInteractor
val Kosmos.activatableNotificationViewModel by Fixture {
ActivatableNotificationViewModel.invoke(
a11yInteractor = accessibilityInteractor,
+ windowRootViewBlurInteractor = windowRootViewBlurInteractor,
)
}
diff --git a/proto/src/metrics_constants/OWNERS b/proto/src/metrics_constants/OWNERS
index 7009282..b032841 100644
--- a/proto/src/metrics_constants/OWNERS
+++ b/proto/src/metrics_constants/OWNERS
@@ -1,4 +1,3 @@
cwren@android.com
-yanglu@google.com
yaochen@google.com
yro@google.com
diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NonNull.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NonNull.java
index db3cd8ed..1153a77 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NonNull.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NonNull.java
@@ -35,14 +35,4 @@
@Retention(SOURCE)
@Target({FIELD, METHOD, PARAMETER, TYPE_USE})
@libcore.api.IntraCoreApi
-public @interface NonNull {
- /**
- * Min Android API level (inclusive) to which this annotation is applied.
- */
- int from() default Integer.MIN_VALUE;
-
- /**
- * Max Android API level to which this annotation is applied.
- */
- int to() default Integer.MAX_VALUE;
-}
+public @interface NonNull {}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/util/Nullable.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/Nullable.java
index 3371978..295f083 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/libcore/util/Nullable.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/Nullable.java
@@ -35,14 +35,4 @@
@Retention(SOURCE)
@Target({FIELD, METHOD, PARAMETER, TYPE_USE})
@libcore.api.IntraCoreApi
-public @interface Nullable {
- /**
- * Min Android API level (inclusive) to which this annotation is applied.
- */
- int from() default Integer.MIN_VALUE;
-
- /**
- * Max Android API level to which this annotation is applied.
- */
- int to() default Integer.MAX_VALUE;
-}
+public @interface Nullable {}
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
index 735635c..b41ce0f 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
@@ -268,18 +268,45 @@
}
/**
+ * Write bytecode to pop the 2 uninitialized instances out of the stack
+ * after performing constructor redirection.
+ */
+fun adjustStackForConstructorRedirection(writer: MethodVisitor) {
+ // Stack: { uninitialized, uninitialized, obj }
+ writer.visitInsn(Opcodes.SWAP)
+ // Stack: { uninitialized, obj, uninitialized }
+ writer.visitInsn(Opcodes.POP)
+ // Stack: { uninitialized, obj }
+ writer.visitInsn(Opcodes.SWAP)
+ // Stack: { obj, uninitialized }
+ writer.visitInsn(Opcodes.POP)
+ // Stack: { obj }
+
+ // We end up with only the desired object on the stack
+}
+
+/**
* Given a method descriptor, insert an [argType] as the first argument to it.
*/
fun prependArgTypeToMethodDescriptor(methodDescriptor: String, classInternalName: String): String {
val returnType = Type.getReturnType(methodDescriptor)
val argTypes = Type.getArgumentTypes(methodDescriptor).toMutableList()
- argTypes.add(0, Type.getType("L" + classInternalName + ";"))
+ argTypes.add(0, Type.getType("L$classInternalName;"))
return Type.getMethodDescriptor(returnType, *argTypes.toTypedArray())
}
/**
+ * Given a method descriptor, change the return type to [classInternalName].
+ */
+fun changeMethodDescriptorReturnType(methodDescriptor: String, classInternalName: String): String {
+ val argTypes = Type.getArgumentTypes(methodDescriptor)
+ val returnType = Type.getType("L$classInternalName;")
+ return Type.getMethodDescriptor(returnType, *argTypes)
+}
+
+/**
* Return the "visibility" modifier from an `access` integer.
*
* (see https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.1-200-E.1)
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt
index c5a2f9f..bba4681 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt
@@ -15,6 +15,7 @@
*/
package com.android.hoststubgen.filters
+import com.android.hoststubgen.log
import org.objectweb.asm.commons.Remapper
/**
@@ -23,19 +24,25 @@
class FilterRemapper(val filter: OutputFilter) : Remapper() {
private val cache = mutableMapOf<String, String>()
- override fun mapType(typeInternalName: String?): String? {
+
+ override fun map(typeInternalName: String?): String? {
if (typeInternalName == null) {
return null
}
cache[typeInternalName]?.let {
+ // log.d("Cached rename from $typeInternalName to $it")
return it
}
- var mapped = filter.remapType(typeInternalName) ?: typeInternalName
+ var mapped = filter.remapType(typeInternalName)
+ if (mapped != null) {
+ log.d("Renaming type $typeInternalName to $mapped")
+ } else {
+ // log.d("Not renaming type $typeInternalName")
+ }
+ mapped = mapped ?: typeInternalName
cache[typeInternalName] = mapped
return mapped
}
-
- // TODO Do we need to implement mapPackage(), etc too?
}
\ No newline at end of file
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
index d0c97c0..dd353e9 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
@@ -100,7 +100,6 @@
methodName: String,
methodDesc: String,
replaceSpec: TextFilePolicyMethodReplaceFilter.MethodCallReplaceSpec,
- policy: FilterPolicyWithReason,
)
}
@@ -286,9 +285,10 @@
methodName: String,
methodDesc: String,
replaceSpec: TextFilePolicyMethodReplaceFilter.MethodCallReplaceSpec,
- policy: FilterPolicyWithReason,
) {
- imf.setPolicyForMethod(className, methodName, methodDesc, policy)
+ // Keep the source method, because the target method may call it.
+ imf.setPolicyForMethod(className, methodName, methodDesc,
+ FilterPolicy.Keep.withReason(FILTER_REASON))
methodReplaceSpec.add(replaceSpec)
}
}
@@ -642,7 +642,6 @@
methodName,
signature,
spec,
- policyWithReason,
)
} else {
// It's an in-class replace.
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt
index a78c655..bc90d12 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt
@@ -15,7 +15,6 @@
*/
package com.android.hoststubgen.filters
-import com.android.hoststubgen.log
import java.util.regex.Pattern
/**
@@ -34,17 +33,12 @@
val typeInternalNamePrefix: String,
)
- private val cache = mutableMapOf<String, String>()
-
override fun remapType(className: String): String? {
- var mapped: String = className
typeRenameSpecs.forEach {
if (it.typeInternalNamePattern.matcher(className).matches()) {
- mapped = it.typeInternalNamePrefix + className
- log.d("Renaming type $className to $mapped")
+ return it.typeInternalNamePrefix + className
}
}
- cache[className] = mapped
- return mapped
+ return null
}
}
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
index 70e7d46..b8a3576 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
@@ -17,7 +17,10 @@
import com.android.hoststubgen.asm.CLASS_INITIALIZER_DESC
import com.android.hoststubgen.asm.CLASS_INITIALIZER_NAME
+import com.android.hoststubgen.asm.CTOR_NAME
import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.adjustStackForConstructorRedirection
+import com.android.hoststubgen.asm.changeMethodDescriptorReturnType
import com.android.hoststubgen.asm.prependArgTypeToMethodDescriptor
import com.android.hoststubgen.asm.writeByteCodeToPushArguments
import com.android.hoststubgen.asm.writeByteCodeToReturn
@@ -33,6 +36,7 @@
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes
import org.objectweb.asm.Opcodes.INVOKEINTERFACE
+import org.objectweb.asm.Opcodes.INVOKESPECIAL
import org.objectweb.asm.Opcodes.INVOKESTATIC
import org.objectweb.asm.Opcodes.INVOKEVIRTUAL
import org.objectweb.asm.Type
@@ -376,53 +380,90 @@
val callerMethodName: String,
next: MethodVisitor?,
) : MethodVisitor(OPCODE_VERSION, next) {
- override fun visitMethodInsn(
+
+ private fun doReplace(
opcode: Int,
- owner: String?,
- name: String?,
- descriptor: String?,
- isInterface: Boolean,
- ) {
+ owner: String,
+ name: String,
+ descriptor: String,
+ ): Boolean {
when (opcode) {
INVOKESTATIC, INVOKEVIRTUAL, INVOKEINTERFACE -> {}
- else -> {
- // Don't touch other opcodes.
- super.visitMethodInsn(opcode, owner, name, descriptor, isInterface)
- return
- }
+ // We only support INVOKESPECIAL when replacing constructors.
+ INVOKESPECIAL -> if (name != CTOR_NAME) return false
+ // Don't touch other opcodes.
+ else -> return false
}
+
val to = filter.getMethodCallReplaceTo(
- currentClassName, callerMethodName, owner!!, name!!, descriptor!!
+ currentClassName, callerMethodName, owner, name, descriptor
)
if (to == null
// Don't replace if the target is the callsite.
|| (to.className == currentClassName && to.methodName == callerMethodName)
) {
- super.visitMethodInsn(opcode, owner, name, descriptor, isInterface)
- return
+ return false
}
- // Replace the method call with a (static) call to the target method.
- // If it's a non-static call, the target method's first argument will receive "this".
- // (Because of that, we don't need to manipulate the stack. Just replace the
- // method call.)
+ if (opcode != INVOKESPECIAL) {
+ // It's either a static method call or virtual method call.
+ // Either way, we don't manipulate the stack and send the original arguments
+ // as is to the target method.
+ //
+ // If the call is a virtual call (INVOKEVIRTUAL or INVOKEINTERFACE), then
+ // the first argument in the stack is the "this" object, so the target
+ // method must have an extra argument as the first argument to receive it.
+ // We update the method descriptor with prependArgTypeToMethodDescriptor()
+ // to absorb this difference.
- val toDesc = if (opcode == INVOKESTATIC) {
- // Static call to static call, no need to change the desc.
- descriptor
+ val toDesc = if (opcode == INVOKESTATIC) {
+ descriptor
+ } else {
+ prependArgTypeToMethodDescriptor(descriptor, owner)
+ }
+
+ mv.visitMethodInsn(
+ INVOKESTATIC,
+ to.className,
+ to.methodName,
+ toDesc,
+ false
+ )
} else {
- // Need to prepend the "this" type to the descriptor.
- prependArgTypeToMethodDescriptor(descriptor, owner)
+ // Because an object initializer does not return a value, the newly created
+ // but uninitialized object will be dup-ed at the bottom of the stack.
+ // We first call the target method to consume the constructor arguments at the top.
+
+ val toDesc = changeMethodDescriptorReturnType(descriptor, owner)
+
+ // Before stack: { uninitialized, uninitialized, args... }
+ mv.visitMethodInsn(
+ INVOKESTATIC,
+ to.className,
+ to.methodName,
+ toDesc,
+ false
+ )
+ // After stack: { uninitialized, uninitialized, obj }
+
+ // Next we pop the 2 uninitialized instances out of the stack.
+ adjustStackForConstructorRedirection(mv)
}
- mv.visitMethodInsn(
- INVOKESTATIC,
- to.className,
- to.methodName,
- toDesc,
- false
- )
+ return true
+ }
+
+ override fun visitMethodInsn(
+ opcode: Int,
+ owner: String,
+ name: String,
+ descriptor: String,
+ isInterface: Boolean,
+ ) {
+ if (!doReplace(opcode, owner, name, descriptor)) {
+ super.visitMethodInsn(opcode, owner, name, descriptor, isInterface)
+ }
}
}
}
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/01-hoststubgen-test-tiny-framework-orig-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/01-hoststubgen-test-tiny-framework-orig-dump.txt
index 635f66d..2b942a9 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -1883,6 +1883,42 @@
InnerClasses:
public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester.class
+ Compiled from "TinyFrameworkMethodCallReplace.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester
+ minor version: 0
+ major version: 65
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 1, attributes: 3
+Constant pool:
+{
+ public int i;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iload_1
+ x: putfield #x // Field i:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ 0 10 1 i I
+}
+SourceFile: "TinyFrameworkMethodCallReplace.java"
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+InnerClasses:
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.class
Compiled from "TinyFrameworkMethodCallReplace.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo
@@ -1891,7 +1927,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 3, attributes: 3
+ interfaces: 0, fields: 0, methods: 4, attributes: 3
Constant pool:
{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo();
@@ -1937,10 +1973,28 @@
Start Length Slot Name Signature
0 4 0 a I
0 4 1 b I
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester newConstructorTester(int);
+ descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ x: dup
+ x: iload_0
+ x: iconst_1
+ x: iadd
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester."<init>":(I)V
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 11 0 i I
}
SourceFile: "TinyFrameworkMethodCallReplace.java"
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
InnerClasses:
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.class
Compiled from "TinyFrameworkMethodCallReplace.java"
@@ -1950,7 +2004,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 5, attributes: 5
+ interfaces: 0, fields: 0, methods: 6, attributes: 5
Constant pool:
{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace();
@@ -2008,6 +2062,21 @@
x: ireturn
LineNumberTable:
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester constructorReplaceTester(int);
+ descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ x: dup
+ x: iload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester."<init>":(I)V
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 9 0 i I
+
private static int originalAdd(int, int);
descriptor: (II)I
flags: (0x000a) ACC_PRIVATE, ACC_STATIC
@@ -2046,6 +2115,7 @@
android.hosttest.annotation.HostSideTestWholeClassKeep
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
BootstrapMethods:
x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
@@ -2053,8 +2123,9 @@
#x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.lambda$nonStaticMethodCallReplaceTester$0:(Ljava/util/concurrent/atomic/AtomicBoolean;)V
#x ()V
InnerClasses:
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
- public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+ public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
Compiled from "TinyFrameworkNative.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
@@ -3198,7 +3269,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 2, attributes: 2
+ interfaces: 0, fields: 0, methods: 3, attributes: 2
Constant pool:
{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller();
@@ -3229,6 +3300,22 @@
LocalVariableTable:
Start Length Slot Name Signature
0 12 0 value I
+
+ public static int bar(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=2, locals=1, args_size=1
+ x: iload_0
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getArray:(I)[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+ x: iconst_0
+ x: aaload
+ x: invokevirtual #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getValue:()I
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 10 0 value I
}
SourceFile: "TinyFrameworkRenamedClassCaller.java"
RuntimeInvisibleAnnotations:
@@ -3242,7 +3329,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 2, attributes: 2
+ interfaces: 0, fields: 1, methods: 3, attributes: 2
Constant pool:
{
private final int mValue;
@@ -3278,6 +3365,26 @@
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed[] getArray(int);
+ descriptor: (I)[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=6, locals=1, args_size=1
+ x: iconst_1
+ x: anewarray #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ x: dup
+ x: iconst_0
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ x: dup
+ x: iload_0
+ x: invokespecial #x // Method "<init>":(I)V
+ x: aastore
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 16 0 value I
}
SourceFile: "TinyFrameworkToBeRenamed.java"
RuntimeInvisibleAnnotations:
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/03-hoststubgen-test-tiny-framework-host-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/03-hoststubgen-test-tiny-framework-host-dump.txt
index 51a3355..d493ad1 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/03-hoststubgen-test-tiny-framework-host-dump.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/03-hoststubgen-test-tiny-framework-host-dump.txt
@@ -2002,6 +2002,51 @@
#x ()Ljava/lang/Integer;
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester.class
+ Compiled from "TinyFrameworkMethodCallReplace.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester
+ minor version: 0
+ major version: 65
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 1, attributes: 4
+Constant pool:
+{
+ public int i;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iload_1
+ x: putfield #x // Field i:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ 0 10 1 i I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+SourceFile: "TinyFrameworkMethodCallReplace.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.class
Compiled from "TinyFrameworkMethodCallReplace.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo
@@ -2010,7 +2055,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 3, attributes: 4
+ interfaces: 0, fields: 0, methods: 4, attributes: 4
Constant pool:
{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo();
@@ -2065,9 +2110,30 @@
RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester newConstructorTester(int);
+ descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ x: dup
+ x: iload_0
+ x: iconst_1
+ x: iadd
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester."<init>":(I)V
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 11 0 i I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
- public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+ public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
SourceFile: "TinyFrameworkMethodCallReplace.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -2081,7 +2147,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 4, attributes: 6
+ interfaces: 0, fields: 0, methods: 6, attributes: 6
Constant pool:
{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace();
@@ -2148,6 +2214,48 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester constructorReplaceTester(int);
+ descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ x: dup
+ x: iload_0
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.newConstructorTester:(I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ x: swap
+ x: pop
+ x: swap
+ x: pop
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 13 0 i I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ private static int originalAdd(int, int);
+ descriptor: (II)I
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=2, args_size=2
+ x: iload_0
+ x: iload_1
+ x: iadd
+ x: iconst_1
+ x: isub
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 6 0 a I
+ 0 6 1 b I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
private static void lambda$nonStaticMethodCallReplaceTester$0(java.util.concurrent.atomic.AtomicBoolean);
descriptor: (Ljava/util/concurrent/atomic/AtomicBoolean;)V
flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
@@ -2167,6 +2275,7 @@
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
SourceFile: "TinyFrameworkMethodCallReplace.java"
@@ -2184,6 +2293,7 @@
#x ()V
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
Compiled from "TinyFrameworkNative.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
@@ -3392,7 +3502,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 2, attributes: 3
+ interfaces: 0, fields: 0, methods: 3, attributes: 3
Constant pool:
{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller();
@@ -3429,6 +3539,25 @@
RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ public static int bar(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=2, locals=1, args_size=1
+ x: iload_0
+ x: invokestatic #x // Method rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getArray:(I)[Lrename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+ x: iconst_0
+ x: aaload
+ x: invokevirtual #x // Method rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getValue:()I
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 10 0 value I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
SourceFile: "TinyFrameworkRenamedClassCaller.java"
RuntimeVisibleAnnotations:
@@ -3867,7 +3996,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 2, attributes: 3
+ interfaces: 0, fields: 1, methods: 3, attributes: 3
Constant pool:
{
private final int mValue;
@@ -3891,7 +4020,7 @@
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+ 0 10 0 this Lrename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
0 10 1 value I
RuntimeVisibleAnnotations:
x: #x()
@@ -3908,7 +4037,30 @@
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+ 0 5 0 this Lrename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ public static rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed[] getArray(int);
+ descriptor: (I)[Lrename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=6, locals=1, args_size=1
+ x: iconst_1
+ x: anewarray #x // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ x: dup
+ x: iconst_0
+ x: new #x // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ x: dup
+ x: iload_0
+ x: invokespecial #x // Method "<init>":(I)V
+ x: aastore
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 16 0 value I
RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/13-hoststubgen-test-tiny-framework-host-ext-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
index a466a2e..8978a7a 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
@@ -2413,6 +2413,66 @@
#x ()Ljava/lang/Integer;
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester.class
+ Compiled from "TinyFrameworkMethodCallReplace.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester
+ minor version: 0
+ major version: 65
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 4
+Constant pool:
+{
+ public int i;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ x: ldc #x // String <init>
+ x: ldc #x // String (I)V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iload_1
+ x: putfield #x // Field i:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ 11 10 1 i I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+SourceFile: "TinyFrameworkMethodCallReplace.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.class
Compiled from "TinyFrameworkMethodCallReplace.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo
@@ -2421,7 +2481,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 4, attributes: 4
+ interfaces: 0, fields: 0, methods: 5, attributes: 4
Constant pool:
{
private static {};
@@ -2501,8 +2561,34 @@
RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester newConstructorTester(int);
+ descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+ x: ldc #x // String newConstructorTester
+ x: ldc #x // String (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ x: dup
+ x: iload_0
+ x: iconst_1
+ x: iadd
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester."<init>":(I)V
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 11 0 i I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
SourceFile: "TinyFrameworkMethodCallReplace.java"
RuntimeVisibleAnnotations:
@@ -2517,7 +2603,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 5, attributes: 6
+ interfaces: 0, fields: 0, methods: 7, attributes: 6
Constant pool:
{
private static {};
@@ -2609,18 +2695,70 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester constructorReplaceTester(int);
+ descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+ x: ldc #x // String constructorReplaceTester
+ x: ldc #x // String (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ x: dup
+ x: iload_0
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.newConstructorTester:(I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ x: swap
+ x: pop
+ x: swap
+ x: pop
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 13 0 i I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ private static int originalAdd(int, int);
+ descriptor: (II)I
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+ x: ldc #x // String originalAdd
+ x: ldc #x // String (II)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iload_0
+ x: iload_1
+ x: iadd
+ x: iconst_1
+ x: isub
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 6 0 a I
+ 11 6 1 b I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
private static void lambda$nonStaticMethodCallReplaceTester$0(java.util.concurrent.atomic.AtomicBoolean);
descriptor: (Ljava/util/concurrent/atomic/AtomicBoolean;)V
flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
Code:
stack=4, locals=1, args_size=1
x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
- x: ldc #x // String lambda$nonStaticMethodCallReplaceTester$0
- x: ldc #x // String (Ljava/util/concurrent/atomic/AtomicBoolean;)V
+ x: ldc #x // String lambda$nonStaticMethodCallReplaceTester$0
+ x: ldc #x // String (Ljava/util/concurrent/atomic/AtomicBoolean;)V
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
x: aload_0
- x: invokestatic #x // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
+ x: invokestatic #x // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
x: invokevirtual #x // Method java/lang/Thread.isDaemon:()Z
x: invokevirtual #x // Method java/util/concurrent/atomic/AtomicBoolean.set:(Z)V
x: return
@@ -2633,6 +2771,7 @@
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
SourceFile: "TinyFrameworkMethodCallReplace.java"
@@ -2650,6 +2789,7 @@
#x ()V
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
Compiled from "TinyFrameworkNative.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
@@ -4241,7 +4381,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 3, attributes: 3
+ interfaces: 0, fields: 0, methods: 4, attributes: 3
Constant pool:
{
private static {};
@@ -4298,6 +4438,30 @@
RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ public static int bar(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
+ x: ldc #x // String bar
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iload_0
+ x: invokestatic #x // Method rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getArray:(I)[Lrename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+ x: iconst_0
+ x: aaload
+ x: invokevirtual #x // Method rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getValue:()I
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 10 0 value I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
SourceFile: "TinyFrameworkRenamedClassCaller.java"
RuntimeVisibleAnnotations:
@@ -4947,7 +5111,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 3, attributes: 3
+ interfaces: 0, fields: 1, methods: 4, attributes: 3
Constant pool:
{
private final int mValue;
@@ -4962,8 +5126,8 @@
flags: (0x000a) ACC_PRIVATE, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: ldc #x // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
@@ -4972,7 +5136,7 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=2, args_size=2
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ x: ldc #x // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
x: ldc #x // String <init>
x: ldc #x // String (I)V
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
@@ -4986,7 +5150,7 @@
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 11 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+ 11 10 0 this Lrename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
11 10 1 value I
RuntimeVisibleAnnotations:
x: #x()
@@ -4997,7 +5161,7 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ x: ldc #x // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
x: ldc #x // String getValue
x: ldc #x // String ()I
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
@@ -5008,7 +5172,35 @@
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+ 11 5 0 this Lrename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ public static rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed[] getArray(int);
+ descriptor: (I)[Lrename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=6, locals=1, args_size=1
+ x: ldc #x // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ x: ldc #x // String getArray
+ x: ldc #x // String (I)[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iconst_1
+ x: anewarray #x // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ x: dup
+ x: iconst_0
+ x: new #x // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ x: dup
+ x: iload_0
+ x: invokespecial #x // Method "<init>":(I)V
+ x: aastore
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 16 0 value I
RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
index 78341d7..406c611 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -1883,6 +1883,42 @@
InnerClasses:
public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester.class
+ Compiled from "TinyFrameworkMethodCallReplace.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 1, attributes: 3
+Constant pool:
+{
+ public int i;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iload_1
+ x: putfield #x // Field i:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ 0 10 1 i I
+}
+SourceFile: "TinyFrameworkMethodCallReplace.java"
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+InnerClasses:
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.class
Compiled from "TinyFrameworkMethodCallReplace.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo
@@ -1891,7 +1927,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 3, attributes: 3
+ interfaces: 0, fields: 0, methods: 4, attributes: 3
Constant pool:
{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo();
@@ -1937,10 +1973,28 @@
Start Length Slot Name Signature
0 4 0 a I
0 4 1 b I
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester newConstructorTester(int);
+ descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ x: dup
+ x: iload_0
+ x: iconst_1
+ x: iadd
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester."<init>":(I)V
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 11 0 i I
}
SourceFile: "TinyFrameworkMethodCallReplace.java"
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
InnerClasses:
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.class
Compiled from "TinyFrameworkMethodCallReplace.java"
@@ -1950,7 +2004,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 5, attributes: 5
+ interfaces: 0, fields: 0, methods: 6, attributes: 5
Constant pool:
{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace();
@@ -2008,6 +2062,21 @@
x: ireturn
LineNumberTable:
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester constructorReplaceTester(int);
+ descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ x: dup
+ x: iload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester."<init>":(I)V
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 9 0 i I
+
private static int originalAdd(int, int);
descriptor: (II)I
flags: (0x000a) ACC_PRIVATE, ACC_STATIC
@@ -2046,6 +2115,7 @@
android.hosttest.annotation.HostSideTestWholeClassKeep
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
BootstrapMethods:
x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
@@ -2053,8 +2123,9 @@
#x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.lambda$nonStaticMethodCallReplaceTester$0:(Ljava/util/concurrent/atomic/AtomicBoolean;)V
#x ()V
InnerClasses:
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
- public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+ public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
Compiled from "TinyFrameworkNative.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
@@ -3219,7 +3290,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 2, attributes: 2
+ interfaces: 0, fields: 0, methods: 3, attributes: 2
Constant pool:
{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller();
@@ -3250,6 +3321,22 @@
LocalVariableTable:
Start Length Slot Name Signature
0 12 0 value I
+
+ public static int bar(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=2, locals=1, args_size=1
+ x: iload_0
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getArray:(I)[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+ x: iconst_0
+ x: aaload
+ x: invokevirtual #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getValue:()I
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 10 0 value I
}
SourceFile: "TinyFrameworkRenamedClassCaller.java"
RuntimeInvisibleAnnotations:
@@ -3263,7 +3350,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 2, attributes: 2
+ interfaces: 0, fields: 1, methods: 3, attributes: 2
Constant pool:
{
private final int mValue;
@@ -3299,6 +3386,26 @@
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed[] getArray(int);
+ descriptor: (I)[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=6, locals=1, args_size=1
+ x: iconst_1
+ x: anewarray #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ x: dup
+ x: iconst_0
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ x: dup
+ x: iload_0
+ x: invokespecial #x // Method "<init>":(I)V
+ x: aastore
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 16 0 value I
}
SourceFile: "TinyFrameworkToBeRenamed.java"
RuntimeInvisibleAnnotations:
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt
index 2e0b182..6a8e488 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt
@@ -2002,6 +2002,51 @@
#x ()Ljava/lang/Integer;
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester.class
+ Compiled from "TinyFrameworkMethodCallReplace.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 1, attributes: 4
+Constant pool:
+{
+ public int i;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iload_1
+ x: putfield #x // Field i:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ 0 10 1 i I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+SourceFile: "TinyFrameworkMethodCallReplace.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.class
Compiled from "TinyFrameworkMethodCallReplace.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo
@@ -2010,7 +2055,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 3, attributes: 4
+ interfaces: 0, fields: 0, methods: 4, attributes: 4
Constant pool:
{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo();
@@ -2065,9 +2110,30 @@
RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester newConstructorTester(int);
+ descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ x: dup
+ x: iload_0
+ x: iconst_1
+ x: iadd
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester."<init>":(I)V
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 11 0 i I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
- public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+ public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
SourceFile: "TinyFrameworkMethodCallReplace.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -2081,7 +2147,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 4, attributes: 6
+ interfaces: 0, fields: 0, methods: 6, attributes: 6
Constant pool:
{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace();
@@ -2148,6 +2214,48 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester constructorReplaceTester(int);
+ descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ x: dup
+ x: iload_0
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.newConstructorTester:(I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ x: swap
+ x: pop
+ x: swap
+ x: pop
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 13 0 i I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ private static int originalAdd(int, int);
+ descriptor: (II)I
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=2, args_size=2
+ x: iload_0
+ x: iload_1
+ x: iadd
+ x: iconst_1
+ x: isub
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 6 0 a I
+ 0 6 1 b I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
private static void lambda$nonStaticMethodCallReplaceTester$0(java.util.concurrent.atomic.AtomicBoolean);
descriptor: (Ljava/util/concurrent/atomic/AtomicBoolean;)V
flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
@@ -2167,6 +2275,7 @@
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
SourceFile: "TinyFrameworkMethodCallReplace.java"
@@ -2184,6 +2293,7 @@
#x ()V
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
Compiled from "TinyFrameworkNative.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
@@ -3422,7 +3532,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 2, attributes: 3
+ interfaces: 0, fields: 0, methods: 3, attributes: 3
Constant pool:
{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller();
@@ -3459,6 +3569,25 @@
RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ public static int bar(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=2, locals=1, args_size=1
+ x: iload_0
+ x: invokestatic #x // Method rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getArray:(I)[Lrename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+ x: iconst_0
+ x: aaload
+ x: invokevirtual #x // Method rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getValue:()I
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 10 0 value I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
SourceFile: "TinyFrameworkRenamedClassCaller.java"
RuntimeVisibleAnnotations:
@@ -3897,7 +4026,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 2, attributes: 3
+ interfaces: 0, fields: 1, methods: 3, attributes: 3
Constant pool:
{
private final int mValue;
@@ -3921,7 +4050,7 @@
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+ 0 10 0 this Lrename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
0 10 1 value I
RuntimeVisibleAnnotations:
x: #x()
@@ -3938,7 +4067,30 @@
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+ 0 5 0 this Lrename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ public static rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed[] getArray(int);
+ descriptor: (I)[Lrename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=6, locals=1, args_size=1
+ x: iconst_1
+ x: anewarray #x // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ x: dup
+ x: iconst_0
+ x: new #x // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ x: dup
+ x: iload_0
+ x: invokespecial #x // Method "<init>":(I)V
+ x: aastore
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 16 0 value I
RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
index 51f7925..d8e7632 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
@@ -2413,6 +2413,66 @@
#x ()Ljava/lang/Integer;
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester.class
+ Compiled from "TinyFrameworkMethodCallReplace.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 4
+Constant pool:
+{
+ public int i;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ x: ldc #x // String <init>
+ x: ldc #x // String (I)V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iload_1
+ x: putfield #x // Field i:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ 11 10 1 i I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+SourceFile: "TinyFrameworkMethodCallReplace.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.class
Compiled from "TinyFrameworkMethodCallReplace.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo
@@ -2421,7 +2481,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 4, attributes: 4
+ interfaces: 0, fields: 0, methods: 5, attributes: 4
Constant pool:
{
private static {};
@@ -2501,8 +2561,34 @@
RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester newConstructorTester(int);
+ descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+ x: ldc #x // String newConstructorTester
+ x: ldc #x // String (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ x: dup
+ x: iload_0
+ x: iconst_1
+ x: iadd
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester."<init>":(I)V
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 11 0 i I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
SourceFile: "TinyFrameworkMethodCallReplace.java"
RuntimeVisibleAnnotations:
@@ -2517,7 +2603,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 5, attributes: 6
+ interfaces: 0, fields: 0, methods: 7, attributes: 6
Constant pool:
{
private static {};
@@ -2609,18 +2695,70 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester constructorReplaceTester(int);
+ descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+ x: ldc #x // String constructorReplaceTester
+ x: ldc #x // String (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ x: dup
+ x: iload_0
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.newConstructorTester:(I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ x: swap
+ x: pop
+ x: swap
+ x: pop
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 13 0 i I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ private static int originalAdd(int, int);
+ descriptor: (II)I
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+ x: ldc #x // String originalAdd
+ x: ldc #x // String (II)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iload_0
+ x: iload_1
+ x: iadd
+ x: iconst_1
+ x: isub
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 6 0 a I
+ 11 6 1 b I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
private static void lambda$nonStaticMethodCallReplaceTester$0(java.util.concurrent.atomic.AtomicBoolean);
descriptor: (Ljava/util/concurrent/atomic/AtomicBoolean;)V
flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
Code:
stack=4, locals=1, args_size=1
x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
- x: ldc #x // String lambda$nonStaticMethodCallReplaceTester$0
- x: ldc #x // String (Ljava/util/concurrent/atomic/AtomicBoolean;)V
+ x: ldc #x // String lambda$nonStaticMethodCallReplaceTester$0
+ x: ldc #x // String (Ljava/util/concurrent/atomic/AtomicBoolean;)V
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
x: aload_0
- x: invokestatic #x // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
+ x: invokestatic #x // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
x: invokevirtual #x // Method java/lang/Thread.isDaemon:()Z
x: invokevirtual #x // Method java/util/concurrent/atomic/AtomicBoolean.set:(Z)V
x: return
@@ -2633,6 +2771,7 @@
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
SourceFile: "TinyFrameworkMethodCallReplace.java"
@@ -2650,6 +2789,7 @@
#x ()V
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
Compiled from "TinyFrameworkNative.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
@@ -4271,7 +4411,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 3, attributes: 3
+ interfaces: 0, fields: 0, methods: 4, attributes: 3
Constant pool:
{
private static {};
@@ -4328,6 +4468,30 @@
RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ public static int bar(int);
+ descriptor: (I)I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
+ x: ldc #x // String bar
+ x: ldc #x // String (I)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iload_0
+ x: invokestatic #x // Method rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getArray:(I)[Lrename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+ x: iconst_0
+ x: aaload
+ x: invokevirtual #x // Method rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getValue:()I
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 10 0 value I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
SourceFile: "TinyFrameworkRenamedClassCaller.java"
RuntimeVisibleAnnotations:
@@ -4977,7 +5141,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
super_class: #x // java/lang/Object
- interfaces: 0, fields: 1, methods: 3, attributes: 3
+ interfaces: 0, fields: 1, methods: 4, attributes: 3
Constant pool:
{
private final int mValue;
@@ -4992,8 +5156,8 @@
flags: (0x000a) ACC_PRIVATE, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
- x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: ldc #x // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
x: return
@@ -5002,7 +5166,7 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=2, args_size=2
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ x: ldc #x // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
x: ldc #x // String <init>
x: ldc #x // String (I)V
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
@@ -5016,7 +5180,7 @@
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 11 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+ 11 10 0 this Lrename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
11 10 1 value I
RuntimeVisibleAnnotations:
x: #x()
@@ -5027,7 +5191,7 @@
flags: (0x0001) ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
- x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ x: ldc #x // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
x: ldc #x // String getValue
x: ldc #x // String ()I
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
@@ -5038,7 +5202,35 @@
LineNumberTable:
LocalVariableTable:
Start Length Slot Name Signature
- 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+ 11 5 0 this Lrename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ public static rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed[] getArray(int);
+ descriptor: (I)[Lrename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=6, locals=1, args_size=1
+ x: ldc #x // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ x: ldc #x // String getArray
+ x: ldc #x // String (I)[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iconst_1
+ x: anewarray #x // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ x: dup
+ x: iconst_0
+ x: new #x // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+ x: dup
+ x: iload_0
+ x: invokespecial #x // Method "<init>":(I)V
+ x: aastore
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 16 0 value I
RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
index 1f64a3c..cbaad2e 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
@@ -68,6 +68,10 @@
class java.lang.Thread keep
method start ()V @com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo.startThread
+# Used to test constructor replacement.
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester keepclass
+ method <init> (I)V @com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo.newConstructorTester
+
# "rename" takes a type internal name, so '/'s is used as a separator.
# The leading / in the prefix is not needed (it'll be stripped), but it's added to make
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java
index 57c69a3..d850be8 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java
@@ -41,10 +41,23 @@
return originalAdd(1, 2);
}
+ public static ConstructorTester constructorReplaceTester(int i) {
+ // This object construction will be replaced with ReplaceTo.newConstructorTester().
+ return new ConstructorTester(i);
+ }
+
private static int originalAdd(int a, int b) {
return a + b - 1; // Original is broken.
}
+ public static class ConstructorTester {
+ public int i;
+
+ public ConstructorTester(int i) {
+ this.i = i;
+ }
+ }
+
public static class ReplaceTo {
public static void startThread(Thread thread) {
thread.setDaemon(true);
@@ -54,5 +67,9 @@
public static int add(int a, int b) {
return a + b;
}
+
+ public static ConstructorTester newConstructorTester(int i) {
+ return new ConstructorTester(i + 1);
+ }
}
}
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java
index 707bc0e..74e4610 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java
@@ -25,4 +25,9 @@
// so this code should work as-is.
return new TinyFrameworkToBeRenamed(value).getValue();
}
+
+ /** Calls the class that'll be renamed. */
+ public static int bar(int value) {
+ return TinyFrameworkToBeRenamed.getArray(value)[0].getValue();
+ }
}
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java
index 8319ced..7dcc83e 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java
@@ -31,4 +31,8 @@
public int getValue() {
return mValue;
}
+
+ public static TinyFrameworkToBeRenamed[] getArray(int value) {
+ return new TinyFrameworkToBeRenamed[] { new TinyFrameworkToBeRenamed(value) };
+ }
}
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
index 68673dc..89fcd30 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
@@ -308,6 +308,11 @@
}
@Test
+ public void testTypeRenameArray() {
+ assertThat(TinyFrameworkRenamedClassCaller.bar(2)).isEqualTo(2);
+ }
+
+ @Test
public void testMethodCallReplaceNonStatic() throws Exception {
assertThat(TinyFrameworkMethodCallReplace.nonStaticMethodCallReplaceTester())
.isEqualTo(true);
@@ -318,4 +323,10 @@
assertThat(TinyFrameworkMethodCallReplace.staticMethodCallReplaceTester())
.isEqualTo(3);
}
+
+ @Test
+ public void testConstructorCallReplace() throws Exception {
+ assertThat(TinyFrameworkMethodCallReplace.constructorReplaceTester(5).i)
+ .isEqualTo(6);
+ }
}
diff --git a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/PtaProcessor.kt b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/PtaProcessor.kt
index b6089ea..a7f481a 100644
--- a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/PtaProcessor.kt
+++ b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/PtaProcessor.kt
@@ -449,7 +449,6 @@
methodName: String,
methodDesc: String,
replaceSpec: TextFilePolicyMethodReplaceFilter.MethodCallReplaceSpec,
- policy: FilterPolicyWithReason
) {
// This can't be converted to an annotation.
classHasMember = true
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 1c95184..42834ce 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2137,9 +2137,6 @@
this, 0, oldUserState.mUserId));
}
- // Announce user changes only if more than one exist.
- final boolean announceNewUser = mUserManager.getUsers().size() > 1;
-
// The user changed.
mCurrentUserId = userId;
AccessibilityUserState userState = getCurrentUserStateLocked();
@@ -2166,13 +2163,6 @@
// As an initialization step, update the shortcuts for the current user.
updateShortcutsForCurrentNavigationMode();
- if (announceNewUser) {
- // Schedule announcement of the current user if needed.
- mMainHandler.sendMessageDelayed(
- obtainMessage(AccessibilityManagerService::announceNewUserIfNeeded, this),
- WAIT_FOR_USER_STATE_FULLY_INITIALIZED_MILLIS);
- }
-
for (IUserInitializationCompleteCallback callback
: mUserInitializationCompleteCallbacks) {
try {
@@ -2186,20 +2176,6 @@
}
}
- private void announceNewUserIfNeeded() {
- synchronized (mLock) {
- AccessibilityUserState userState = getCurrentUserStateLocked();
- if (userState.isHandlingAccessibilityEventsLocked()) {
- String message = mContext.getString(R.string.user_switched,
- mUserManager.getUserInfo(mCurrentUserId).name);
- AccessibilityEvent event = AccessibilityEvent.obtain(
- AccessibilityEvent.TYPE_ANNOUNCEMENT);
- event.getText().add(message);
- sendAccessibilityEventLocked(event, mCurrentUserId);
- }
- }
- }
-
private void unlockUser(int userId) {
synchronized (mLock) {
int parentUserId = mSecurityPolicy.resolveProfileParentLocked(userId);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index fdadaaf..5395d2a 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2625,6 +2625,13 @@
}
notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
r.foregroundNoti = notification;
+ if (r.isForeground && foregroundServiceType != previousFgsType) {
+ // An already foreground service is being started with a different fgs type
+ // which results in the type changing without typical startForeground
+ // logging.
+ Slog.w(TAG_SERVICE, "FGS type change for " + r.shortInstanceName
+ + " from " + previousFgsType + " to " + foregroundServiceType);
+ }
mAm.mProcessStateController.setForegroundServiceType(r, foregroundServiceType);
if (!r.isForeground) {
final ServiceMap smap = getServiceMapLocked(r.userId);
@@ -9191,7 +9198,9 @@
} else {
synchronized (mAm.mPidsSelfLocked) {
callerApp = mAm.mPidsSelfLocked.get(callingPid);
- caller = callerApp.getThread();
+ if (callerApp != null) {
+ caller = callerApp.getThread();
+ }
}
}
if (callerApp == null) {
@@ -9358,10 +9367,17 @@
+ "for package "
+ packageName);
}
- setServiceForegroundInnerLocked(sr, sr.foregroundId,
- sr.foregroundNoti, /* flags */ 0,
- ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK,
- /* callingUidStart */ 0, /* systemRequestedTransition */ true);
+ try {
+ setServiceForegroundInnerLocked(sr, sr.foregroundId,
+ sr.foregroundNoti, /* flags */ 0,
+ ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK,
+ /* callingUidStart */ 0, /* systemRequestedTransition */ true);
+ } catch (Exception e) {
+ Slog.w(TAG,
+ "Exception in system initiated foreground service transition "
+ + "for package " + packageName
+ + ":" + e.toString());
+ }
} else {
if (DEBUG_FOREGROUND_SERVICE) {
Slog.d(TAG,
@@ -9411,10 +9427,17 @@
+ "for package "
+ packageName);
}
- setServiceForegroundInnerLocked(sr, /* id */ 0,
- /* notification */ null, /* flags */ 0,
- /* foregroundServiceType */ 0, /* callingUidStart */ 0,
- /* systemRequestedTransition */ true);
+ try {
+ setServiceForegroundInnerLocked(sr, /* id */ 0,
+ /* notification */ null, /* flags */ 0,
+ /* foregroundServiceType */ 0, /* callingUidStart */ 0,
+ /* systemRequestedTransition */ true);
+ } catch (Exception e) {
+ Slog.wtf(TAG,
+ "Exception in system initiated background service transition "
+ + "for package " + packageName
+ + ":" + e.toString());
+ }
} else {
if (DEBUG_FOREGROUND_SERVICE) {
Slog.d(TAG, "Ignoring system initiated transition of foreground"
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index f1007e7..0954c49 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -238,6 +238,8 @@
"pixel_connectivity_gps",
"pixel_continuity",
"pixel_display",
+ "pixel_fingerprint",
+ "pixel_gsc",
"pixel_perf",
"pixel_sensai",
"pixel_sensors",
@@ -245,6 +247,7 @@
"pixel_system_sw_video",
"pixel_video_sw",
"pixel_watch",
+ "pixel_wifi",
"platform_compat",
"platform_security",
"pixel_watch_debug_trace",
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index c8b8909..f7d7ed5 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -3006,7 +3006,7 @@
UidState uidState = getUidStateLocked(uid, false);
if (uidState != null) {
int rawUidMode = mAppOpsCheckingService.getUidMode(
- uidState.uid, getPersistentId(virtualDeviceId), code);
+ uidState.uid, getPersistentDeviceIdForOp(virtualDeviceId, code), code);
if (rawUidMode != AppOpsManager.opToDefaultMode(code)) {
return raw ? rawUidMode :
@@ -3069,7 +3069,7 @@
int switchCode = AppOpsManager.opToSwitch(code);
int rawUidMode = mAppOpsCheckingService.getUidMode(uid,
- getPersistentId(virtualDeviceId), switchCode);
+ getPersistentDeviceIdForOp(virtualDeviceId, switchCode), switchCode);
if (rawUidMode != AppOpsManager.opToDefaultMode(switchCode)) {
return raw ? rawUidMode : evaluateForegroundMode(uid, switchCode, rawUidMode);
@@ -3396,7 +3396,7 @@
}
final Op op = getOpLocked(ops, code, uid, true);
final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag,
- getPersistentId(virtualDeviceId));
+ getPersistentDeviceIdForOp(virtualDeviceId, code));
if (attributedOp.isRunning()) {
Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName + " code "
+ code + " startTime of in progress event="
@@ -3418,15 +3418,15 @@
// If there is a non-default per UID policy (we set UID op mode only if
// non-default) it takes over, otherwise use the per package policy.
- } else if (mAppOpsCheckingService.getUidMode(
- uidState.uid, getPersistentId(virtualDeviceId), switchCode)
+ } else if (mAppOpsCheckingService.getUidMode(uidState.uid,
+ getPersistentDeviceIdForOp(virtualDeviceId, switchCode), switchCode)
!= AppOpsManager.opToDefaultMode(switchCode)) {
final int uidMode =
uidState.evalMode(
code,
mAppOpsCheckingService.getUidMode(
uidState.uid,
- getPersistentId(virtualDeviceId),
+ getPersistentDeviceIdForOp(virtualDeviceId, switchCode),
switchCode));
if (uidMode != AppOpsManager.MODE_ALLOWED) {
if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code "
@@ -3478,7 +3478,8 @@
virtualDeviceId, flags, AppOpsManager.MODE_ALLOWED);
attributedOp.accessed(proxyUid, proxyPackageName, proxyAttributionTag,
- getPersistentId(proxyVirtualDeviceId), uidState.getState(), flags, notedCount);
+ getPersistentDeviceIdForOp(proxyVirtualDeviceId, code), uidState.getState(),
+ flags, notedCount);
if (shouldCollectAsyncNotedOp) {
collectAsyncNotedOp(uid, packageName, code, attributionTag, flags, message,
@@ -4045,7 +4046,7 @@
}
final Op op = getOpLocked(ops, code, uid, true);
final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag,
- getPersistentId(virtualDeviceId));
+ getPersistentDeviceIdForOp(virtualDeviceId, code));
final UidState uidState = ops.uidState;
isRestricted = isOpRestrictedLocked(uid, code, packageName, attributionTag,
virtualDeviceId, pvr.bypass, false);
@@ -4058,8 +4059,9 @@
// If there is a non-default per UID policy (we set UID op mode only if
// non-default) it takes over, otherwise use the per package policy.
} else if ((rawUidMode =
- mAppOpsCheckingService.getUidMode(
- uidState.uid, getPersistentId(virtualDeviceId), switchCode))
+ mAppOpsCheckingService.getUidMode(
+ uidState.uid, getPersistentDeviceIdForOp(virtualDeviceId, switchCode),
+ switchCode))
!= AppOpsManager.opToDefaultMode(switchCode)) {
final int uidMode = uidState.evalMode(code, rawUidMode);
if (!shouldStartForMode(uidMode, startIfModeDefault)) {
@@ -4107,11 +4109,13 @@
try {
if (isRestricted) {
attributedOp.createPaused(clientId, virtualDeviceId, proxyUid, proxyPackageName,
- proxyAttributionTag, getPersistentId(proxyVirtualDeviceId),
+ proxyAttributionTag,
+ getPersistentDeviceIdForOp(proxyVirtualDeviceId, code),
uidState.getState(), flags, attributionFlags, attributionChainId);
} else {
attributedOp.started(clientId, virtualDeviceId, proxyUid, proxyPackageName,
- proxyAttributionTag, getPersistentId(proxyVirtualDeviceId),
+ proxyAttributionTag,
+ getPersistentDeviceIdForOp(proxyVirtualDeviceId, code),
uidState.getState(), flags, attributionFlags, attributionChainId);
startType = START_TYPE_STARTED;
}
@@ -4179,15 +4183,15 @@
final int switchCode = AppOpsManager.opToSwitch(code);
// If there is a non-default mode per UID policy (we set UID op mode only if
// non-default) it takes over, otherwise use the per package policy.
- if (mAppOpsCheckingService.getUidMode(
- uidState.uid, getPersistentId(virtualDeviceId), switchCode)
+ if (mAppOpsCheckingService.getUidMode(uidState.uid,
+ getPersistentDeviceIdForOp(virtualDeviceId, switchCode), switchCode)
!= AppOpsManager.opToDefaultMode(switchCode)) {
final int uidMode =
uidState.evalMode(
code,
mAppOpsCheckingService.getUidMode(
uidState.uid,
- getPersistentId(virtualDeviceId),
+ getPersistentDeviceIdForOp(virtualDeviceId, switchCode),
switchCode));
if (!shouldStartForMode(uidMode, startIfModeDefault)) {
if (DEBUG) {
@@ -4350,7 +4354,8 @@
return;
}
final AttributedOp attributedOp =
- op.mDeviceAttributedOps.getOrDefault(getPersistentId(virtualDeviceId),
+ op.mDeviceAttributedOps.getOrDefault(
+ getPersistentDeviceIdForOp(virtualDeviceId, code),
new ArrayMap<>()).get(attributionTag);
if (attributedOp == null) {
Slog.e(TAG, "Attribution not found: uid=" + uid + " pkg=" + packageName + "("
@@ -4641,7 +4646,8 @@
return true;
}
if (mVirtualDeviceManagerInternal == null) {
- return true;
+ Slog.w(TAG, "VirtualDeviceManagerInternal is null when device Id is non-default");
+ return false;
}
if (mVirtualDeviceManagerInternal.isValidVirtualDeviceId(virtualDeviceId)) {
mKnownDeviceIds.put(virtualDeviceId,
@@ -7310,7 +7316,13 @@
return packageNames;
}
- @NonNull private String getPersistentId(int virtualDeviceId) {
+ // For ops associated with device aware permissions, if virtual device id is non-default, we
+ // will return string version of that id provided the virtual device has corresponding camera or
+ // audio policy.
+ @NonNull private String getPersistentDeviceIdForOp(int virtualDeviceId, int op) {
+ virtualDeviceId = PermissionManager.resolveDeviceIdForPermissionCheck(mContext,
+ virtualDeviceId, AppOpsManager.opToPermission(op));
+
if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) {
return PERSISTENT_DEVICE_ID_DEFAULT;
}
@@ -7319,6 +7331,7 @@
}
String persistentId =
mVirtualDeviceManagerInternal.getPersistentIdForDevice(virtualDeviceId);
+
if (persistentId == null) {
persistentId = mKnownDeviceIds.get(virtualDeviceId);
}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 964b97c..551202c 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -1051,7 +1051,9 @@
void handleHdrSdrNitsChanged(float displayNits, float sdrNits) {
final float newHdrSdrRatio;
- if (displayNits != INVALID_NITS && sdrNits != INVALID_NITS) {
+ if (displayNits != INVALID_NITS && sdrNits != INVALID_NITS
+ && (mBacklightAdapter.mUseSurfaceControlBrightness ||
+ mBacklightAdapter.mForceSurfaceControl)) {
// Ensure the ratio stays >= 1.0f as values below that are nonsensical
newHdrSdrRatio = Math.max(1.f, displayNits / sdrNits);
} else {
diff --git a/services/core/java/com/android/server/display/color/OWNERS b/services/core/java/com/android/server/display/color/OWNERS
index 27adf12..8f5a9a0 100644
--- a/services/core/java/com/android/server/display/color/OWNERS
+++ b/services/core/java/com/android/server/display/color/OWNERS
@@ -1,4 +1,3 @@
christyfranks@google.com
-justinklaassen@google.com
-per-file DisplayTransformManager.java=michaelwr@google.com
\ No newline at end of file
+per-file DisplayTransformManager.java=michaelwr@google.com
diff --git a/services/core/java/com/android/server/display/mode/ModeChangeObserver.java b/services/core/java/com/android/server/display/mode/ModeChangeObserver.java
index bbc13cc..2751835 100644
--- a/services/core/java/com/android/server/display/mode/ModeChangeObserver.java
+++ b/services/core/java/com/android/server/display/mode/ModeChangeObserver.java
@@ -23,8 +23,6 @@
import android.view.DisplayAddress;
import android.view.DisplayEventReceiver;
-import com.android.internal.annotations.KeepForWeakReference;
-
import java.util.HashSet;
import java.util.Set;
@@ -35,7 +33,6 @@
private final DisplayModeDirector.Injector mInjector;
@SuppressWarnings("unused")
- @KeepForWeakReference
private DisplayEventReceiver mModeChangeListener;
private final SparseArray<Set<Integer>> mRejectedModesByDisplay = new SparseArray<>();
private Looper mLooper;
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 7e8bb28..144caea 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -1365,6 +1365,21 @@
}
}
+ @Override
+ public void setScreensaverEnabled(boolean enabled) {
+ checkPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS);
+ final UserHandle userHandle = getCallingUserHandle();
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.SCREENSAVER_ENABLED, enabled ? 1 : 0,
+ userHandle.getIdentifier());
+ mPowerManagerInternal.updateSettings();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
boolean canLaunchDreamActivity(String dreamPackageName, String packageName,
int callingUid) {
if (dreamPackageName == null || packageName == null) {
diff --git a/services/core/java/com/android/server/input/InputDataStore.java b/services/core/java/com/android/server/input/InputDataStore.java
index 834f815..6ed50b6 100644
--- a/services/core/java/com/android/server/input/InputDataStore.java
+++ b/services/core/java/com/android/server/input/InputDataStore.java
@@ -89,6 +89,10 @@
final InputStream inputStream = mInputGestureFileInjector.openRead(userId);
inputGestureDataList = readInputGesturesXml(inputStream, false);
inputStream.close();
+ } catch (FileNotFoundException exception) {
+ // There are valid reasons for the file to be missing, such as shortcuts having not
+ // been registered by the user.
+ return List.of();
} catch (IOException exception) {
// In case we are unable to read from the file on disk or another IO operation error,
// fail gracefully.
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index 4f3aa06..280f3b7 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -120,12 +120,21 @@
* Binding flags for establishing connection to the {@link InputMethodService}.
*/
@VisibleForTesting
- static final int IME_CONNECTION_BIND_FLAGS =
- Context.BIND_AUTO_CREATE
+ static final int IME_CONNECTION_BIND_FLAGS;
+ static {
+ if (android.view.inputmethod.Flags.lowerImeOomImportance()) {
+ IME_CONNECTION_BIND_FLAGS = Context.BIND_AUTO_CREATE
+ | Context.BIND_ALMOST_PERCEPTIBLE
+ | Context.BIND_IMPORTANT_BACKGROUND
+ | Context.BIND_SCHEDULE_LIKE_TOP_APP;
+ } else {
+ IME_CONNECTION_BIND_FLAGS = Context.BIND_AUTO_CREATE
| Context.BIND_NOT_VISIBLE
| Context.BIND_NOT_FOREGROUND
| Context.BIND_IMPORTANT_BACKGROUND
| Context.BIND_SCHEDULE_LIKE_TOP_APP;
+ }
+ }
private final int mImeConnectionBindFlags;
diff --git a/services/core/java/com/android/server/inputmethod/OWNERS b/services/core/java/com/android/server/inputmethod/OWNERS
index e507c6b..9d8aef9 100644
--- a/services/core/java/com/android/server/inputmethod/OWNERS
+++ b/services/core/java/com/android/server/inputmethod/OWNERS
@@ -1,7 +1,6 @@
set noparent
roosa@google.com
-yukawa@google.com
tarandeep@google.com
fstern@google.com
cosminbaies@google.com
diff --git a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
index 463989a..60d028b 100644
--- a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
+++ b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
@@ -390,10 +390,11 @@
.max(Comparator.comparingLong(PackageInstaller.SessionInfo::getCreatedMillis));
}
- // ADB sets installerPackageName to null, this creates a loophole to bypass BIC which will be
- // addressed with b/265203007
private boolean installedByAdb(String initiatingPackageName) {
- if(PackageManagerServiceUtils.isInstalledByAdb(initiatingPackageName)) {
+ // GTS tests needs to adopt shell identity to install apps.
+ if(!SystemProperties.get("gts.transparency.bg-install-apps").isEmpty()) {
+ Slog.d(TAG, "handlePackageAdd: is GTS tests, skipping ADB check");
+ } else if(PackageManagerServiceUtils.isInstalledByAdb(initiatingPackageName)) {
Slog.d(TAG, "handlePackageAdd: is installed by ADB, skipping");
return true;
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index f96846c..acdc79f 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -1067,6 +1067,9 @@
void doPostDexopt(List<ReconciledPackage> reconciledPackages,
List<InstallRequest> requests, Map<String, Boolean> createdAppId,
MoveInfo moveInfo, long acquireTime) {
+ for (InstallRequest request : requests) {
+ request.onWaitDexoptFinished();
+ }
boolean success = false;
try {
if (commitInstallPackages(reconciledPackages)) {
@@ -1218,6 +1221,7 @@
CompletableFuture<Void> future =
DexOptHelper.performDexoptIfNeededAsync(request, mDexManager);
completableFutures.add(future);
+ request.onWaitDexoptStarted();
}
if (!completableFutures.isEmpty()) {
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index fbf5db5..7349204 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -1028,6 +1028,18 @@
}
}
+ public void onWaitDexoptStarted() {
+ if (mPackageMetrics != null) {
+ mPackageMetrics.onStepStarted(PackageMetrics.STEP_WAIT_DEXOPT);
+ }
+ }
+
+ public void onWaitDexoptFinished() {
+ if (mPackageMetrics != null) {
+ mPackageMetrics.onStepFinished(PackageMetrics.STEP_WAIT_DEXOPT);
+ }
+ }
+
public void onDexoptFinished(DexoptResult dexoptResult) {
// Only report external profile warnings when installing from adb. The goal is to warn app
// developers if they have provided bad external profiles, so it's not beneficial to report
diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS
index 62b89f32..f98ec04 100644
--- a/services/core/java/com/android/server/pm/OWNERS
+++ b/services/core/java/com/android/server/pm/OWNERS
@@ -1,7 +1,6 @@
hackbod@android.com
hackbod@google.com
jsharkey@android.com
-jsharkey@google.com
narayan@google.com
include /PACKAGE_MANAGER_OWNERS
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 136cb12..635ef06 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -5189,7 +5189,9 @@
"Session " + sessionId + " is a parent of multi-package session and "
+ "requestUserPreapproval on the parent session isn't supported.");
}
-
+ if (statusReceiver == null) {
+ throw new IllegalArgumentException("Status receiver cannot be null.");
+ }
synchronized (mLock) {
assertPreparedAndNotSealedLocked("request of session " + sessionId);
mPreapprovalDetails = details;
@@ -5542,6 +5544,10 @@
*/
private static void sendOnUserActionRequired(Context context, IntentSender target,
int sessionId, Intent intent) {
+ if (target == null) {
+ Slog.e(TAG, "Missing receiver for pending user action.");
+ return;
+ }
final Intent fillIn = new Intent();
fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_PENDING_USER_ACTION);
diff --git a/services/core/java/com/android/server/pm/PackageMetrics.java b/services/core/java/com/android/server/pm/PackageMetrics.java
index 994ee42..22da3b2 100644
--- a/services/core/java/com/android/server/pm/PackageMetrics.java
+++ b/services/core/java/com/android/server/pm/PackageMetrics.java
@@ -73,6 +73,7 @@
public static final int STEP_DEXOPT = 5;
public static final int STEP_FREEZE_INSTALL = 6;
public static final int STEP_RESTORE = 7;
+ public static final int STEP_WAIT_DEXOPT = 8;
@IntDef(prefix = {"STEP_"}, value = {
STEP_PREPARE,
@@ -81,7 +82,8 @@
STEP_COMMIT,
STEP_DEXOPT,
STEP_FREEZE_INSTALL,
- STEP_RESTORE
+ STEP_RESTORE,
+ STEP_WAIT_DEXOPT
})
@Retention(RetentionPolicy.SOURCE)
public @interface StepInt {
diff --git a/services/core/java/com/android/server/pm/dex/OWNERS b/services/core/java/com/android/server/pm/dex/OWNERS
index 5ca8ddd..70af4e7 100644
--- a/services/core/java/com/android/server/pm/dex/OWNERS
+++ b/services/core/java/com/android/server/pm/dex/OWNERS
@@ -1,4 +1,3 @@
-alanstokes@google.com
jiakaiz@google.com
ngeoffray@google.com
mast@google.com
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index e3eced2..dd454cd 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -7445,6 +7445,13 @@
public void setDevicePostured(boolean isPostured) {
setDevicePosturedInternal(isPostured);
}
+
+ @Override
+ public void updateSettings() {
+ synchronized (mLock) {
+ updateSettingsLocked();
+ }
+ }
}
/**
diff --git a/services/core/java/com/android/server/power/stats/BatteryHistoryDirectory.java b/services/core/java/com/android/server/power/stats/BatteryHistoryDirectory.java
index 29cc9ea..5563f98 100644
--- a/services/core/java/com/android/server/power/stats/BatteryHistoryDirectory.java
+++ b/services/core/java/com/android/server/power/stats/BatteryHistoryDirectory.java
@@ -251,6 +251,10 @@
try (FileInputStream stream = file.openRead()) {
byte[] header = new byte[FILE_FORMAT_BYTES];
if (stream.read(header, 0, FILE_FORMAT_BYTES) == -1) {
+ if (file.getBaseFile().length() == 0) {
+ return new byte[0];
+ }
+
Slog.e(TAG, "Invalid battery history file format " + file.getBaseFile());
deleteFragment(fragment);
return null;
diff --git a/services/core/java/com/android/server/uri/OWNERS b/services/core/java/com/android/server/uri/OWNERS
index cdc07ed..6599db7 100644
--- a/services/core/java/com/android/server/uri/OWNERS
+++ b/services/core/java/com/android/server/uri/OWNERS
@@ -1,3 +1,2 @@
jsharkey@android.com
-jsharkey@google.com
varunshah@google.com
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 12f5534..58534b9 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -712,13 +712,17 @@
if (!isMagnifierActivated) {
return;
}
- // All opening/closing situations.
+ // All opening/closing/recents transitions
+ boolean notify = (flags & TRANSIT_FLAG_IS_RECENTS) != 0;
switch (type) {
case WindowManager.TRANSIT_OPEN:
case WindowManager.TRANSIT_TO_FRONT:
case WindowManager.TRANSIT_CLOSE:
case WindowManager.TRANSIT_TO_BACK:
- mUserContextChangedNotifier.onWMTransition(type, flags);
+ notify = true;
+ }
+ if (notify) {
+ mUserContextChangedNotifier.onWMTransition(type, flags);
}
}
@@ -1088,8 +1092,7 @@
// causing the notifying, or the recents/home window is removed, then we won't need the
// delayed notification anymore.
void onWMTransition(@TransitionType int type, @TransitionFlags int flags) {
- if (type == WindowManager.TRANSIT_TO_FRONT
- && (flags & TRANSIT_FLAG_IS_RECENTS) != 0) {
+ if ((flags & TRANSIT_FLAG_IS_RECENTS) != 0) {
// Delay the recents to front transition notification then send after if needed.
mHasDelayedNotificationForRecentsToFrontTransition = true;
} else {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 247264f..bdde5fe 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -58,6 +58,7 @@
import static android.security.Flags.preventIntentRedirectAbortOrThrowException;
import static android.security.Flags.preventIntentRedirectShowToast;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.TRANSIT_FLAG_AVOID_MOVE_TO_FRONT;
import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -1841,6 +1842,9 @@
// no-user-leaving implies not entering PiP.
transition.setCanPipOnFinish(false /* canPipOnFinish */);
}
+ if (avoidMoveToFront() && transition != null) {
+ transition.addFlag(TRANSIT_FLAG_AVOID_MOVE_TO_FRONT);
+ }
if (isIndependentLaunch && transition != null) {
transitionController.requestStartTransition(transition,
mTargetTask == null ? started.getTask() : mTargetTask,
diff --git a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
index 5eed5470..cb82f48 100644
--- a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
@@ -18,6 +18,7 @@
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH;
+import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_SIMULATE_REQUESTED_ORIENTATION;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA;
@@ -183,9 +184,10 @@
private boolean shouldEnableCameraCompatFreeformTreatmentForApp() {
if (mCameraCompatAllowOrientationTreatmentOptProp != null) {
+ // OptProp is not-null iff the opt-out flag is on.
return mCameraCompatAllowOrientationTreatmentOptProp
.shouldEnableWithOptOutOverrideAndProperty(isChangeEnabled(mActivityRecord,
- OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT));
+ OVERRIDE_CAMERA_COMPAT_DISABLE_SIMULATE_REQUESTED_ORIENTATION));
} else {
return isChangeEnabled(mActivityRecord,
OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT);
diff --git a/services/core/java/com/android/server/wm/ClientLifecycleManager.java b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
index d291d99..98e453b 100644
--- a/services/core/java/com/android/server/wm/ClientLifecycleManager.java
+++ b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
@@ -25,6 +25,7 @@
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.os.Build;
+import android.os.DeadObjectException;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.Trace;
@@ -66,64 +67,80 @@
/**
* Schedules a transaction, which may consist of multiple callbacks and a lifecycle request.
* @param transaction A sequence of client transaction items.
- * @throws RemoteException
- *
+ * @return {@code false} if the transaction failed because of {@link RemoteException}.
* @see ClientTransaction
*/
@VisibleForTesting
- void scheduleTransaction(@NonNull ClientTransaction transaction) throws RemoteException {
- final IApplicationThread client = transaction.getClient();
- try {
- transaction.schedule();
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to deliver transaction for " + client
- + "\ntransaction=" + transaction);
- throw e;
+ boolean scheduleTransaction(@NonNull ClientTransaction transaction) {
+ final RemoteException e = transaction.schedule();
+ if (e != null) {
+ final WindowProcessController wpc = mWms.mAtmService.getProcessController(
+ transaction.getClient());
+ Slog.w(TAG, "Failed to deliver transaction for " + wpc + "\ntransaction=" + this, e);
+ return false;
}
+ return true;
}
/**
* Similar to {@link #scheduleTransactionItem}, but it sends the transaction immediately and
* it can be called without WM lock.
*
+ * @return {@code false} if the transaction failed because of {@link RemoteException}.
* @see WindowProcessController#setReportedProcState(int)
*/
- void scheduleTransactionItemNow(@NonNull IApplicationThread client,
+ boolean scheduleTransactionItemNow(@NonNull IApplicationThread client,
@NonNull ClientTransactionItem transactionItem) throws RemoteException {
final ClientTransaction clientTransaction = new ClientTransaction(client);
clientTransaction.addTransactionItem(transactionItem);
- scheduleTransaction(clientTransaction);
+ final boolean res = scheduleTransaction(clientTransaction);
+ if (!com.android.window.flags.Flags.cleanupDispatchPendingTransactionsRemoteException()
+ && !res) {
+ throw new DeadObjectException();
+ }
+ return res;
}
/**
- * Schedules a transaction with the given item, delivery to client application.
+ * Schedules a transaction with the given item, delivery to client application, which may be
+ * queued to dispatched later.
*
- * @throws RemoteException
+ * @return {@code false} if the transaction was dispatched immediately, but failed because of
+ * {@link RemoteException}. If the transaction is queued, any failure will be ignored.
* @see ClientTransactionItem
*/
- void scheduleTransactionItem(@NonNull IApplicationThread client,
+ boolean scheduleTransactionItem(@NonNull IApplicationThread client,
@NonNull ClientTransactionItem item) throws RemoteException {
// Wait until RootWindowContainer#performSurfacePlacementNoTrace to dispatch all pending
// transactions at once.
final ClientTransaction clientTransaction = getOrCreatePendingTransaction(client);
clientTransaction.addTransactionItem(item);
- onClientTransactionItemScheduled(clientTransaction, false /* shouldDispatchImmediately */);
+ final boolean res = onClientTransactionItemScheduled(clientTransaction,
+ false /* shouldDispatchImmediately */);
+ if (!com.android.window.flags.Flags.cleanupDispatchPendingTransactionsRemoteException()
+ && !res) {
+ throw new DeadObjectException();
+ }
+ return res;
}
/**
- * Schedules a transaction with the given items, delivery to client application.
+ * Schedules a transaction with the given items, delivery to client application, which may be
+ * queued to dispatched later.
*
- * @throws RemoteException
+ * @return {@code false} if the transaction was dispatched immediately, but failed because of
+ * {@link RemoteException}. If the transaction is queued, any failure will be ignored.
* @see ClientTransactionItem
*/
- void scheduleTransactionItems(@NonNull IApplicationThread client,
+ boolean scheduleTransactionItems(@NonNull IApplicationThread client,
@NonNull ClientTransactionItem... items) throws RemoteException {
- scheduleTransactionItems(client, false /* shouldDispatchImmediately */, items);
+ return scheduleTransactionItems(client, false /* shouldDispatchImmediately */, items);
}
/**
- * Schedules a transaction with the given items, delivery to client application.
+ * Schedules a transaction with the given items, delivery to client application, which may be
+ * queued to dispatched later.
*
* @param shouldDispatchImmediately whether or not to dispatch the transaction immediately. This
* should only be {@code true} when it is important to know the
@@ -131,11 +148,11 @@
* launches an app, the server needs to know if the transaction
* is dispatched successfully, and may restart the process if
* not.
- *
- * @throws RemoteException
+ * @return {@code false} if the transaction was dispatched immediately, but failed because of
+ * {@link RemoteException}. If the transaction is queued, any failure will be ignored.
* @see ClientTransactionItem
*/
- void scheduleTransactionItems(@NonNull IApplicationThread client,
+ boolean scheduleTransactionItems(@NonNull IApplicationThread client,
boolean shouldDispatchImmediately,
@NonNull ClientTransactionItem... items) throws RemoteException {
// Wait until RootWindowContainer#performSurfacePlacementNoTrace to dispatch all pending
@@ -147,7 +164,13 @@
clientTransaction.addTransactionItem(items[i]);
}
- onClientTransactionItemScheduled(clientTransaction, shouldDispatchImmediately);
+ final boolean res = onClientTransactionItemScheduled(clientTransaction,
+ shouldDispatchImmediately);
+ if (!com.android.window.flags.Flags.cleanupDispatchPendingTransactionsRemoteException()
+ && !res) {
+ throw new DeadObjectException();
+ }
+ return res;
}
/** Executes all the pending transactions. */
@@ -159,12 +182,7 @@
final int size = mPendingTransactions.size();
for (int i = 0; i < size; i++) {
final ClientTransaction transaction = mPendingTransactions.valueAt(i);
- try {
- scheduleTransaction(transaction);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to deliver pending transaction", e);
- // TODO(b/323801078): apply cleanup for individual transaction item if needed.
- }
+ scheduleTransaction(transaction);
}
mPendingTransactions.clear();
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
@@ -174,12 +192,7 @@
void dispatchPendingTransaction(@NonNull IApplicationThread client) {
final ClientTransaction pendingTransaction = mPendingTransactions.remove(client.asBinder());
if (pendingTransaction != null) {
- try {
- scheduleTransaction(pendingTransaction);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to deliver pending transaction", e);
- // TODO(b/323801078): apply cleanup for individual transaction item if needed.
- }
+ scheduleTransaction(pendingTransaction);
}
}
@@ -211,15 +224,22 @@
return transaction;
}
- /** Must only be called with WM lock. */
- private void onClientTransactionItemScheduled(
+ /**
+ * Must only be called with WM lock.
+ * If the transaction should not be queued, it will be dispatched immediately.
+ *
+ * @return {@code false} if the transaction was dispatched immediately, but failed because of
+ * {@link RemoteException}.
+ */
+ private boolean onClientTransactionItemScheduled(
@NonNull ClientTransaction clientTransaction,
- boolean shouldDispatchImmediately) throws RemoteException {
+ boolean shouldDispatchImmediately) {
if (shouldDispatchImmediately || shouldDispatchPendingTransactionsImmediately()) {
// Dispatch the pending transaction immediately.
mPendingTransactions.remove(clientTransaction.getClient().asBinder());
- scheduleTransaction(clientTransaction);
+ return scheduleTransaction(clientTransaction);
}
+ return true;
}
/** Must only be called with WM lock. */
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index d6ae651..c26acec 100644
--- a/services/core/java/com/android/server/wm/ContentRecorder.java
+++ b/services/core/java/com/android/server/wm/ContentRecorder.java
@@ -490,7 +490,7 @@
return null;
}
final Task taskToRecord = wc.asTask();
- if (taskToRecord == null) {
+ if (taskToRecord == null || !taskToRecord.isAttached()) {
handleStartRecordingFailed();
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
"Content Recording: Unable to retrieve task to start recording for "
diff --git a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
index b6f74a0..a4eeb68 100644
--- a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
@@ -16,6 +16,10 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.DesktopModeHelper.canEnterDesktopMode;
@@ -23,10 +27,13 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityOptions;
+import android.app.WindowConfiguration;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.util.Slog;
+import android.window.DesktopModeFlags;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wm.LaunchParamsController.LaunchParamsModifier;
/**
* The class that defines default launch params for tasks in desktop mode
@@ -74,6 +81,13 @@
appendLog("task null, skipping");
return RESULT_SKIP;
}
+
+ if (DesktopModeFlags.DISABLE_DESKTOP_LAUNCH_PARAMS_OUTSIDE_DESKTOP_BUG_FIX.isTrue()
+ && !isEnteringDesktopMode(task, options, currentParams)) {
+ appendLog("not entering desktop mode, skipping");
+ return RESULT_SKIP;
+ }
+
if (com.android.window.flags.Flags.fixLayoutExistingTask()
&& task.getCreatedByOrganizerTask() != null) {
appendLog("has created-by-organizer-task, skipping");
@@ -122,6 +136,62 @@
return RESULT_CONTINUE;
}
+ /**
+ * Returns true if a task is entering desktop mode, due to its windowing mode being freeform or
+ * if there exists other freeform tasks on the display.
+ */
+ @VisibleForTesting
+ boolean isEnteringDesktopMode(
+ @NonNull Task task,
+ @Nullable ActivityOptions options,
+ @NonNull LaunchParamsController.LaunchParams currentParams) {
+ // As freeform tasks cannot exist outside of desktop mode, it is safe to assume if
+ // freeform tasks are visible we are in desktop mode and as a result any launching
+ // activity will also enter desktop mode. On this same relationship, we can also assume
+ // if there are not visible freeform tasks but a freeform activity is now launching, it
+ // will force the device into desktop mode.
+ return (task.getDisplayContent().getTopMostVisibleFreeformActivity() != null
+ && checkSourceWindowModesCompatible(task, options, currentParams))
+ || isRequestingFreeformWindowMode(task, options, currentParams);
+ }
+
+ private boolean isRequestingFreeformWindowMode(
+ @NonNull Task task,
+ @Nullable ActivityOptions options,
+ @NonNull LaunchParamsController.LaunchParams currentParams) {
+ return task.inFreeformWindowingMode()
+ || (options != null && options.getLaunchWindowingMode() == WINDOWING_MODE_FREEFORM)
+ || (currentParams.hasWindowingMode()
+ && currentParams.mWindowingMode == WINDOWING_MODE_FREEFORM);
+ }
+
+ /**
+ * Returns true is all possible source window modes are compatible with desktop mode.
+ */
+ private boolean checkSourceWindowModesCompatible(
+ @NonNull Task task,
+ @Nullable ActivityOptions options,
+ @NonNull LaunchParamsController.LaunchParams currentParams) {
+ return isCompatibleDesktopWindowingMode(task.getWindowingMode())
+ && (options == null
+ || isCompatibleDesktopWindowingMode(options.getLaunchWindowingMode()))
+ && isCompatibleDesktopWindowingMode(currentParams.mWindowingMode);
+ }
+
+ /**
+ * Returns true is the requesting window mode is one that can lead to the activity entering
+ * desktop.
+ */
+ private boolean isCompatibleDesktopWindowingMode(
+ @WindowConfiguration.WindowingMode int windowingMode) {
+ return switch (windowingMode) {
+ case WINDOWING_MODE_UNDEFINED,
+ WINDOWING_MODE_FULLSCREEN,
+ WINDOWING_MODE_FREEFORM -> true;
+ default -> false;
+ };
+ }
+
private void initLogBuilder(Task task, ActivityRecord activity) {
if (DEBUG) {
mLogBuilder = new StringBuilder(
diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS
index dede767..243a532 100644
--- a/services/core/java/com/android/server/wm/OWNERS
+++ b/services/core/java/com/android/server/wm/OWNERS
@@ -3,7 +3,6 @@
ogunwale@google.com
jjaggi@google.com
racarr@google.com
-chaviw@google.com
vishnun@google.com
akulian@google.com
roosa@google.com
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index d0d2067..1c3510d 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2007,6 +2007,11 @@
return getActivity(r -> !r.finishing, true /* traverseTopToBottom */);
}
+ ActivityRecord getTopMostVisibleFreeformActivity() {
+ return getActivity(r -> r.isVisibleRequested() && r.inFreeformWindowingMode(),
+ true /* traverseTopToBottom */);
+ }
+
ActivityRecord getTopActivity(boolean includeFinishing, boolean includeOverlays) {
// Break down into 4 calls to avoid object creation due to capturing input params.
if (includeFinishing) {
diff --git a/services/core/jni/stats/OWNERS b/services/core/jni/stats/OWNERS
index 2611e5b..8d87925 100644
--- a/services/core/jni/stats/OWNERS
+++ b/services/core/jni/stats/OWNERS
@@ -1,8 +1,6 @@
jeffreyhuang@google.com
-jtnguyen@google.com
muhammadq@google.com
sharaieko@google.com
singhtejinder@google.com
tsaichristine@google.com
yaochen@google.com
-yro@google.com
diff --git a/services/musicrecognition/OWNERS b/services/musicrecognition/OWNERS
index 037b048..820be00 100644
--- a/services/musicrecognition/OWNERS
+++ b/services/musicrecognition/OWNERS
@@ -1,5 +1,4 @@
# Bug component: 830636
oni@google.com
-volnov@google.com
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml
index 5fe5b23..d6a6853 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml
@@ -18,6 +18,9 @@
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-instrumentation" />
+ <!-- Needed for reading the app files for the test artifacts -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="install-arg" value="-t" />
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 301a754..9d4fe9e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -41,8 +41,8 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.content.pm.ApplicationInfo.CATEGORY_SOCIAL;
import static android.content.pm.ApplicationInfo.CATEGORY_GAME;
+import static android.content.pm.ApplicationInfo.CATEGORY_SOCIAL;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.UI_MODE_TYPE_DESK;
@@ -66,7 +66,6 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
@@ -196,7 +195,7 @@
// Because the booted state is set, avoid starting real home if there is no task.
doReturn(false).when(mRootWindowContainer).resumeHomeActivity(any(), anyString(), any());
// Do not execute the transaction, because we can't verify the parameter after it recycles.
- doNothing().when(mClientLifecycleManager).scheduleTransaction(any());
+ doReturn(true).when(mClientLifecycleManager).scheduleTransaction(any());
}
private TestStartingWindowOrganizer registerTestStartingWindowOrganizer() {
@@ -266,7 +265,7 @@
break;
}
}
- return null;
+ return true;
}).when(mClientLifecycleManager).scheduleTransaction(any());
activity.setState(STOPPED, "testPausingWhenVisibleFromStopped");
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
index d4be7f8..51da511 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
@@ -18,6 +18,7 @@
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH;
+import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_SIMULATE_REQUESTED_ORIENTATION;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA;
@@ -231,6 +232,7 @@
@Test
@EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @DisableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT)
public void testShouldApplyCameraCompatFreeformTreatment_notEnabledByOverride_returnsFalse() {
runTestScenario((robot) -> {
robot.activity().createActivityWithComponentInNewTask();
@@ -240,21 +242,19 @@
}
@Test
- @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_SIMULATE_REQUESTED_ORIENTATION})
@EnableFlags({FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING,
FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT})
- public void testShouldApplyCameraCompatFreeformTreatment_propertyFalse_returnsFalse() {
+ public void testShouldApplyCameraCompatFreeformTreatment_disablePropertyOn_returnsFalse() {
runTestScenario((robot) -> {
robot.activity().createActivityWithComponentInNewTask();
- robot.prop().disable(PROPERTY_CAMERA_COMPAT_ALLOW_SIMULATE_REQUESTED_ORIENTATION);
-
robot.checkShouldApplyFreeformTreatmentForCameraCompat(false);
});
}
@Test
- @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
+ @EnableCompatChanges(OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT)
@EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
@DisableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT)
public void testShouldApplyCameraCompatFreeformTreatment_optOutFlagNotEnabled_optOutIgnored() {
@@ -262,18 +262,50 @@
robot.activity().createActivityWithComponentInNewTask();
robot.prop().disable(PROPERTY_CAMERA_COMPAT_ALLOW_SIMULATE_REQUESTED_ORIENTATION);
+ robot.activity().createActivityWithComponentInNewTask();
robot.checkShouldApplyFreeformTreatmentForCameraCompat(true);
});
}
@Test
- @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
- @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
- public void testShouldApplyCameraCompatFreeformTreatment_overrideAndFlagEnabled_returnsTrue() {
+ @EnableFlags({FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING,
+ FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT})
+ public void testShouldApplyCameraCompatFreeformTreatment_optedOutViaProperty_returnsFalse() {
runTestScenario((robot) -> {
robot.activity().createActivityWithComponentInNewTask();
+ robot.prop().disable(PROPERTY_CAMERA_COMPAT_ALLOW_SIMULATE_REQUESTED_ORIENTATION);
+ robot.activity().createActivityWithComponentInNewTask();
+
+ robot.checkShouldApplyFreeformTreatmentForCameraCompat(false);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges(OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT)
+ @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @DisableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT)
+ public void testShouldApplyCameraCompatFreeformTreatment_optInAndFlagEnabled_returnsTrue() {
+ runTestScenario((robot) -> {
+ robot.activity().createActivityWithComponentInNewTask();
+
+ robot.checkShouldApplyFreeformTreatmentForCameraCompat(true);
+ });
+ }
+
+
+ @Test
+ @EnableFlags({FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING,
+ FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT})
+ public void testShouldApplyCameraCompatFreeformTreatment_notOptedOut_flagEnabled_returnsTrue() {
+ runTestScenario((robot) -> {
+ robot.conf().enableCameraCompatTreatment(true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponentInNewTask();
+ robot.prop().enable(PROPERTY_CAMERA_COMPAT_ALLOW_SIMULATE_REQUESTED_ORIENTATION);
+ });
+
robot.checkShouldApplyFreeformTreatmentForCameraCompat(true);
});
}
@@ -294,6 +326,8 @@
@EnableCompatChanges({OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA,
OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA,
OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
+ @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @DisableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT)
public void testShouldRecomputeConfigurationForFreeformTreatment() {
runTestScenario((robot) -> {
robot.conf().enableCameraCompatSplitScreenAspectRatio(true);
@@ -307,6 +341,24 @@
}
@Test
+ @EnableCompatChanges({OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA,
+ OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
+ @EnableFlags({FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING,
+ FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT})
+ public void testShouldRecomputeConfigurationForFreeformTreatmentWithOptOutMechanism() {
+ runTestScenario((robot) -> {
+ robot.conf().enableCameraCompatSplitScreenAspectRatio(true);
+ robot.conf().enableCameraCompatTreatment(true);
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponentInNewTask();
+ robot.prop().enable(PROPERTY_CAMERA_COMPAT_ALLOW_SIMULATE_REQUESTED_ORIENTATION);
+ });
+
+ robot.checkShouldApplyFreeformTreatmentForCameraCompat(true);
+ });
+ }
+
+ @Test
@EnableCompatChanges({OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA})
public void shouldOverrideMinAspectRatioForCamera_overrideEnabled_returnsTrue() {
runTestScenario((robot) -> {
diff --git a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
index 5607252..50876c7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
@@ -25,6 +25,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
+import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_SIMULATE_REQUESTED_ORIENTATION;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_FULL_USER;
@@ -42,6 +43,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.wm.AppCompatConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO;
import static com.android.window.flags.Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING;
+import static com.android.window.flags.Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -134,8 +136,10 @@
}
@Test
- @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
- public void testIsCameraRunningAndWindowingModeEligible_overrideDisabled_returnsFalse() {
+ @EnableFlags({FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING,
+ FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT})
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_SIMULATE_REQUESTED_ORIENTATION})
+ public void testIsCameraRunningAndWindowingModeEligible_disabledViaOverride_returnsFalse() {
configureActivity(SCREEN_ORIENTATION_PORTRAIT);
mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
@@ -165,6 +169,7 @@
@Test
@EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @DisableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT)
@EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
public void testIsCameraRunningAndWindowingModeEligible_optInFreeformCameraRunning_true() {
configureActivity(SCREEN_ORIENTATION_PORTRAIT);
@@ -175,6 +180,17 @@
}
@Test
+ @EnableFlags({FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING,
+ FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT})
+ public void testIsCameraRunningAndWindowingModeEligible_freeformCameraRunning_true() {
+ configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+
+ onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+
+ assertTrue(mCameraCompatFreeformPolicy.isCameraRunningAndWindowingModeEligible(mActivity));
+ }
+
+ @Test
@EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
public void testIsFreeformLetterboxingForCameraAllowed_overrideDisabled_returnsFalse() {
configureActivity(SCREEN_ORIENTATION_PORTRAIT);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
index 02ad9db..8d214dd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
@@ -16,11 +16,13 @@
package com.android.server.wm;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doThrow;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -32,17 +34,19 @@
import android.app.servertransaction.ActivityLifecycleItem;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.ClientTransactionItem;
+import android.os.DeadObjectException;
import android.os.IBinder;
import android.os.RemoteException;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
+import com.android.window.flags.Flags;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -60,15 +64,11 @@
@Mock
private IApplicationThread mClient;
@Mock
- private IApplicationThread.Stub mNonBinderClient;
- @Mock
private ClientTransaction mTransaction;
@Mock
private ClientTransactionItem mTransactionItem;
@Mock
private ActivityLifecycleItem mLifecycleItem;
- @Captor
- private ArgumentCaptor<ClientTransaction> mTransactionCaptor;
private WindowManagerService mWms;
private ClientLifecycleManager mLifecycleManager;
@@ -83,7 +83,6 @@
doReturn(true).when(mLifecycleItem).isActivityLifecycleItem();
doReturn(mClientBinder).when(mClient).asBinder();
- doReturn(mNonBinderClient).when(mNonBinderClient).asBinder();
}
@Test
@@ -91,13 +90,11 @@
spyOn(mWms.mWindowPlacerLocked);
doReturn(true).when(mWms.mWindowPlacerLocked).isTraversalScheduled();
- // Use non binder client to get non-recycled ClientTransaction.
- mLifecycleManager.scheduleTransactionItem(mNonBinderClient, mTransactionItem);
+ mLifecycleManager.scheduleTransactionItem(mClient, mTransactionItem);
// When there is traversal scheduled, add transaction items to pending.
assertEquals(1, mLifecycleManager.mPendingTransactions.size());
- ClientTransaction transaction =
- mLifecycleManager.mPendingTransactions.get(mNonBinderClient);
+ ClientTransaction transaction = mLifecycleManager.mPendingTransactions.get(mClientBinder);
assertEquals(1, transaction.getTransactionItems().size());
assertEquals(mTransactionItem, transaction.getTransactionItems().get(0));
// TODO(b/324203798): cleanup after remove UnsupportedAppUsage
@@ -108,10 +105,10 @@
// Add new transaction item to the existing pending.
clearInvocations(mLifecycleManager);
- mLifecycleManager.scheduleTransactionItem(mNonBinderClient, mLifecycleItem);
+ mLifecycleManager.scheduleTransactionItem(mClient, mLifecycleItem);
assertEquals(1, mLifecycleManager.mPendingTransactions.size());
- transaction = mLifecycleManager.mPendingTransactions.get(mNonBinderClient);
+ transaction = mLifecycleManager.mPendingTransactions.get(mClientBinder);
assertEquals(2, transaction.getTransactionItems().size());
assertEquals(mTransactionItem, transaction.getTransactionItems().get(0));
assertEquals(mLifecycleItem, transaction.getTransactionItems().get(1));
@@ -124,8 +121,7 @@
@Test
public void testScheduleTransactionItemNow() throws RemoteException {
- // Use non binder client to get non-recycled ClientTransaction.
- mLifecycleManager.scheduleTransactionItemNow(mNonBinderClient, mTransactionItem);
+ mLifecycleManager.scheduleTransactionItemNow(mClient, mTransactionItem);
// Dispatch immediately.
assertTrue(mLifecycleManager.mPendingTransactions.isEmpty());
@@ -137,13 +133,11 @@
spyOn(mWms.mWindowPlacerLocked);
doReturn(true).when(mWms.mWindowPlacerLocked).isTraversalScheduled();
- // Use non binder client to get non-recycled ClientTransaction.
- mLifecycleManager.scheduleTransactionItems(mNonBinderClient, mTransactionItem,
- mLifecycleItem);
+ mLifecycleManager.scheduleTransactionItems(mClient, mTransactionItem, mLifecycleItem);
assertEquals(1, mLifecycleManager.mPendingTransactions.size());
final ClientTransaction transaction =
- mLifecycleManager.mPendingTransactions.get(mNonBinderClient);
+ mLifecycleManager.mPendingTransactions.get(mClientBinder);
assertEquals(2, transaction.getTransactionItems().size());
assertEquals(mTransactionItem, transaction.getTransactionItems().get(0));
assertEquals(mLifecycleItem, transaction.getTransactionItems().get(1));
@@ -160,8 +154,8 @@
spyOn(mWms.mWindowPlacerLocked);
doReturn(true).when(mWms.mWindowPlacerLocked).isTraversalScheduled();
- // Use non binder client to get non-recycled ClientTransaction.
- mLifecycleManager.scheduleTransactionItems(mNonBinderClient,
+ mLifecycleManager.scheduleTransactionItems(
+ mClient,
true /* shouldDispatchImmediately */,
mTransactionItem, mLifecycleItem);
@@ -187,7 +181,7 @@
doReturn(true).when(mWms.mWindowPlacerLocked).isLayoutDeferred();
// Queue transactions during layout deferred.
- mLifecycleManager.scheduleTransactionItem(mNonBinderClient, mLifecycleItem);
+ mLifecycleManager.scheduleTransactionItem(mClient, mLifecycleItem);
verify(mLifecycleManager, never()).scheduleTransaction(any());
@@ -203,4 +197,42 @@
verify(mLifecycleManager).scheduleTransaction(any());
}
+
+ @EnableFlags(Flags.FLAG_CLEANUP_DISPATCH_PENDING_TRANSACTIONS_REMOTE_EXCEPTION)
+ @Test
+ public void testOnRemoteException_returnTrueOnSuccess() throws RemoteException {
+ final boolean res = mLifecycleManager.scheduleTransactionItemNow(mClient, mTransactionItem);
+
+ assertTrue(res);
+ }
+
+ @EnableFlags(Flags.FLAG_CLEANUP_DISPATCH_PENDING_TRANSACTIONS_REMOTE_EXCEPTION)
+ @Test
+ public void testOnRemoteException_returnFalseOnFailure() throws RemoteException {
+ final DeadObjectException e = new DeadObjectException();
+ doThrow(e).when(mClient).scheduleTransaction(any());
+
+ // No exception when flag enabled.
+ final boolean res = mLifecycleManager.scheduleTransactionItemNow(mClient, mTransactionItem);
+
+ assertFalse(res);
+ }
+
+ @EnableFlags(Flags.FLAG_CLEANUP_DISPATCH_PENDING_TRANSACTIONS_REMOTE_EXCEPTION)
+ @Test
+ public void testOnRemoteException_returnTrueForQueueing() throws RemoteException {
+ spyOn(mWms.mWindowPlacerLocked);
+ doReturn(true).when(mWms.mWindowPlacerLocked).isLayoutDeferred();
+ final DeadObjectException e = new DeadObjectException();
+ doThrow(e).when(mClient).scheduleTransaction(any());
+
+ final boolean res = mLifecycleManager.scheduleTransactionItem(mClient, mTransactionItem);
+
+ assertTrue(res);
+
+ doReturn(false).when(mWms.mWindowPlacerLocked).isLayoutDeferred();
+ mLifecycleManager.onLayoutContinued();
+
+ verify(mLifecycleManager).scheduleTransaction(any());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
index d305c2f..3a06fff 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
@@ -49,6 +49,8 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
import android.app.ActivityOptions;
import android.compat.testing.PlatformCompatChangeRule;
@@ -98,7 +100,8 @@
mResult = new LaunchParamsController.LaunchParams();
mResult.reset();
- mTarget = new DesktopModeLaunchParamsModifier(mContext);
+ mTarget = spy(new DesktopModeLaunchParamsModifier(mContext));
+ doReturn(true).when(mTarget).isEnteringDesktopMode(any(), any(), any());
}
@Test
@@ -137,6 +140,81 @@
}
@Test
+ @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ Flags.FLAG_DISABLE_DESKTOP_LAUNCH_PARAMS_OUTSIDE_DESKTOP_BUG_FIX})
+ public void testReturnsSkipIfIsEnteringDesktopModeFalse() {
+ setupDesktopModeLaunchParamsModifier();
+ when(mTarget.isEnteringDesktopMode(any(), any(), any())).thenReturn(false);
+
+ final Task task = new TaskBuilder(mSupervisor).build();
+
+ assertEquals(RESULT_SKIP, new CalculateRequestBuilder().setTask(task).calculate());
+ }
+
+ @Test
+ @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ Flags.FLAG_DISABLE_DESKTOP_LAUNCH_PARAMS_OUTSIDE_DESKTOP_BUG_FIX})
+ public void testReturnsContinueIfVisibleFreeformTaskExists() {
+ setupDesktopModeLaunchParamsModifier();
+ when(mTarget.isEnteringDesktopMode(any(), any(), any())).thenCallRealMethod();
+
+ final DisplayContent dc = spy(createNewDisplay());
+ final Task existingFreeformTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
+ .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+ doReturn(existingFreeformTask.getRootActivity()).when(dc)
+ .getTopMostVisibleFreeformActivity();
+ final Task launchingTask = new TaskBuilder(mSupervisor).build();
+ launchingTask.onDisplayChanged(dc);
+
+ assertEquals(RESULT_CONTINUE,
+ new CalculateRequestBuilder().setTask(launchingTask).calculate());
+ }
+
+ @Test
+ @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ Flags.FLAG_DISABLE_DESKTOP_LAUNCH_PARAMS_OUTSIDE_DESKTOP_BUG_FIX})
+ public void testReturnsContinueIfTaskInFreeform() {
+ setupDesktopModeLaunchParamsModifier();
+ when(mTarget.isEnteringDesktopMode(any(), any(), any())).thenCallRealMethod();
+
+ final Task task = new TaskBuilder(mSupervisor).setWindowingMode(WINDOWING_MODE_FREEFORM)
+ .build();
+
+ assertEquals(RESULT_CONTINUE,
+ new CalculateRequestBuilder().setTask(task).calculate());
+ }
+
+ @Test
+ @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ Flags.FLAG_DISABLE_DESKTOP_LAUNCH_PARAMS_OUTSIDE_DESKTOP_BUG_FIX})
+ public void testReturnsContinueIfFreeformRequestViaActivityOptions() {
+ setupDesktopModeLaunchParamsModifier();
+ when(mTarget.isEnteringDesktopMode(any(), any(), any())).thenCallRealMethod();
+
+ final Task task = new TaskBuilder(mSupervisor).build();
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
+
+ assertEquals(RESULT_CONTINUE,
+ new CalculateRequestBuilder().setTask(task).setOptions(options).calculate());
+ }
+
+ @Test
+ @EnableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ Flags.FLAG_DISABLE_DESKTOP_LAUNCH_PARAMS_OUTSIDE_DESKTOP_BUG_FIX})
+ public void testReturnsContinueIfFreeformRequestViaPreviousModifier() {
+ setupDesktopModeLaunchParamsModifier();
+ when(mTarget.isEnteringDesktopMode(any(), any(), any())).thenCallRealMethod();
+
+ final Task task = new TaskBuilder(mSupervisor).build();
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
+
+ assertEquals(RESULT_CONTINUE,
+ new CalculateRequestBuilder().setTask(task).setOptions(options).calculate());
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
public void testReturnsSkipIfNotBoundsPhase() {
setupDesktopModeLaunchParamsModifier();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index d228970..9dc7026 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -24,7 +24,6 @@
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -295,7 +294,7 @@
@Test
public void testCachedStateConfigurationChange() throws RemoteException {
- doNothing().when(mClientLifecycleManager).scheduleTransactionItemNow(any(), any());
+ doReturn(true).when(mClientLifecycleManager).scheduleTransactionItemNow(any(), any());
final IApplicationThread thread = mWpc.getThread();
final Configuration newConfig = new Configuration(mWpc.getConfiguration());
newConfig.densityDpi += 100;
diff --git a/telecomm/java/android/telecom/OWNERS b/telecomm/java/android/telecom/OWNERS
index 6656a01..0854c5d 100644
--- a/telecomm/java/android/telecom/OWNERS
+++ b/telecomm/java/android/telecom/OWNERS
@@ -3,4 +3,3 @@
rgreenwalt@google.com
tgunn@google.com
breadley@google.com
-hallliu@google.com
diff --git a/tests/AppJankTest/src/android/app/jank/tests/JankTrackerTest.java b/tests/AppJankTest/src/android/app/jank/tests/JankTrackerTest.java
index 1bdf019..9d87fbd 100644
--- a/tests/AppJankTest/src/android/app/jank/tests/JankTrackerTest.java
+++ b/tests/AppJankTest/src/android/app/jank/tests/JankTrackerTest.java
@@ -18,10 +18,12 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import android.app.jank.Flags;
import android.app.jank.JankTracker;
import android.app.jank.StateTracker;
+import android.content.Context;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
@@ -55,10 +57,9 @@
* Start an empty activity so decore view is not null when creating the JankTracker instance.
*/
private static ActivityScenario<EmptyActivity> sEmptyActivityRule;
-
private static String sActivityName;
-
private static View sActivityDecorView;
+ private static Context sContext;
@BeforeClass
public static void classSetup() {
@@ -66,6 +67,7 @@
sEmptyActivityRule.onActivity(activity -> {
sActivityDecorView = activity.getWindow().getDecorView();
sActivityName = activity.toString();
+ sContext = activity.getApplicationContext();
});
}
@@ -168,4 +170,14 @@
assertNotNull(jankTracker);
}
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
+ public void jankTracker_IsNull_WhenViewNotInHierarchy() {
+ TestWidget testWidget = new TestWidget(sContext);
+ JankTracker jankTracker = testWidget.getJankTracker();
+
+ assertNull(jankTracker);
+ }
+
}
diff --git a/tests/EnforcePermission/OWNERS b/tests/EnforcePermission/OWNERS
index 39550a3..160849e 100644
--- a/tests/EnforcePermission/OWNERS
+++ b/tests/EnforcePermission/OWNERS
@@ -1,3 +1,2 @@
# Bug component: 315013
tweek@google.com
-brufino@google.com
diff --git a/tools/hiddenapi/OWNERS b/tools/hiddenapi/OWNERS
index dc82aac..d1e36b9 100644
--- a/tools/hiddenapi/OWNERS
+++ b/tools/hiddenapi/OWNERS
@@ -1,6 +1,5 @@
# compat-team@ for changes to hiddenapi files
mathewi@google.com
-satayev@google.com
# soong-team@ as the files these tools protect are tightly coupled with Soong
file:platform/build/soong:/OWNERS
diff --git a/tools/lint/OWNERS b/tools/lint/OWNERS
index 8e4569e..4035e19 100644
--- a/tools/lint/OWNERS
+++ b/tools/lint/OWNERS
@@ -1,6 +1,5 @@
mattgilbride@google.com
azharaa@google.com
-jsharkey@google.com
per-file *CallingSettingsNonUserGetterMethods* = file:/packages/SettingsProvider/OWNERS
per-file *RegisterReceiverFlagDetector* = jacobhobbie@google.com
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/OWNERS b/wifi/java/src/android/net/wifi/sharedconnectivity/OWNERS
index 2a4acc1..abb9aa4 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/OWNERS
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/OWNERS
@@ -1,4 +1,3 @@
# Bug component: 1216021
asapperstein@google.com
-etancohen@google.com